<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>Java并发体系-第一阶段-多线程基础知识 | 风祈的时光录</title><meta name="keywords" content="Java并发，原理，源码"><meta name="author" content="youthlql"><meta name="copyright" content="youthlql"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="万字系列长文讲解Java并发-第一阶段-多线程基础知识。">
<meta property="og:type" content="article">
<meta property="og:title" content="Java并发体系-第一阶段-多线程基础知识">
<meta property="og:url" content="https://imlql.cn/post/efc79183.html">
<meta property="og:site_name" content="风祈的时光录">
<meta property="og:description" content="万字系列长文讲解Java并发-第一阶段-多线程基础知识。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png">
<meta property="article:published_time" content="2020-10-05T14:40:58.000Z">
<meta property="article:modified_time" content="2022-07-24T11:02:09.800Z">
<meta property="article:author" content="youthlql">
<meta property="article:tag" content="Java并发">
<meta property="article:tag" content="原理">
<meta property="article:tag" content="源码">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png"><link rel="shortcut icon" href="https://npm.elemecdn.com/youthlql@1.0.8/blog/favicon.png"><link rel="canonical" href="https://imlql.cn/post/efc79183"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//hm.baidu.com"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://npm.elemecdn.com/@fortawesome/fontawesome-free@6/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://npm.elemecdn.com/@fancyapps/ui/dist/fancybox.css" media="print" onload="this.media='all'"><script>var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?f693ff99cc7e613b88cf5b729a14b48b";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: true,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'Java并发体系-第一阶段-多线程基础知识',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-07-24 19:02:09'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="https://npm.elemecdn.com/lql_static@latest/butterfly_static/css/ali_icon.css"><link rel="stylesheet" href="https://npm.elemecdn.com/lql_static@latest/butterfly_static/css/2-24-mogai.css"><meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/atom.xml" title="风祈的时光录" type="application/atom+xml">
</head><body><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/avatar/2.jpg" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="site-data is-center"><div class="data-item"><a href="/archives/"><div class="headline">文章</div><div class="length-num">37</div></a></div><div class="data-item"><a href="/tags/"><div class="headline">标签</div><div class="length-num">21</div></a></div><div class="data-item"><a href="/categories/"><div class="headline">分类</div><div class="length-num">13</div></a></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/">风祈的时光录</a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw iconfont icon-shouyex"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw iconfont icon-zuixinwenzhang_huaban"></i><span> 找文章</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/categories/"><i class="fa-fw iconfont icon-fenlei1"></i><span> 分类</span></a></li><li><a class="site-page child" href="/tags/"><i class="fa-fw iconfont icon-biaoqian1"></i><span> 标签</span></a></li><li><a class="site-page child" href="/archives/"><i class="fa-fw iconfont icon-shijianzhou"></i><span> 归档</span></a></li></ul></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">Java并发体系-第一阶段-多线程基础知识</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2020-10-05T14:40:58.000Z" title="发表于 2020-10-05 22:40:58">2020-10-05</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-07-24T11:02:09.800Z" title="更新于 2022-07-24 19:02:09">2022-07-24</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Java%E5%B9%B6%E5%8F%91/">Java并发</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/Java%E5%B9%B6%E5%8F%91/%E5%8E%9F%E7%90%86/">原理</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">13.7k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>53分钟</span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="程序、进程、线程的理解"><a href="#程序、进程、线程的理解" class="headerlink" title="程序、进程、线程的理解"></a>程序、进程、线程的理解</h1><p>1、程序(programm)<br>概念：是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码。</p>
<p>2、进程(process)<br>概念：程序的一次执行过程，或是正在运行的一个程序。<br>说明：进程作为资源分配的单位，系统在运行时会为每个进程分配不同的内存区域</p>
<p>3、线程(thread)<br>概念：进程可进一步细化为线程，是一个程序内部的一条执行路径。<br>说明：线程作为CPU调度和执行的单位，每个线程拥独立的运行栈和程序计数器(pc)，线程切换的开销小。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0001.png">



<p>补充：</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0002.png">

<p>进程可以细化为多个线程。<br>每个线程，拥有自己独立的：栈、程序计数器<br>多个线程，共享同一个进程中的结构：方法区、堆。</p>
<h1 id="并行与并发"><a href="#并行与并发" class="headerlink" title="并行与并发"></a>并行与并发</h1><h2 id="单核CPU与多核CPU的理解"><a href="#单核CPU与多核CPU的理解" class="headerlink" title="单核CPU与多核CPU的理解"></a>单核CPU与多核CPU的理解</h2><ul>
<li>单核CPU，其实是一种假的多线程，因为在一个时间单元内，也只能执行一个线程的任务。例如：虽然有多车道，但是收费站只有一个工作人员在收费，只有收了费才能通过，那么CPU就好比收费人员。如果某个人不想交钱，那么收费人员可以把他“挂起”（晾着他，等他想通了，准备好了钱，再去收费。）但是因为CPU时间单元特别短，因此感觉不出来。</li>
<li>如果是多核的话，才能更好的发挥多线程的效率。（现在的服务器都是多核的）</li>
<li>一个Java应用程序java.exe，其实至少三个线程：main()主线程，gc()垃圾回收线程，异常处理线程。当然如果发生异常，会影响主线程。</li>
</ul>
<h2 id="并行与并发的理解"><a href="#并行与并发的理解" class="headerlink" title="并行与并发的理解"></a>并行与并发的理解</h2><p>并行：多个CPU同时执行多个任务。比如：多个人同时做不同的事。<br>并发：一个CPU(采用时间片)同时执行多个任务。比如：秒杀、多个人做同一件事</p>
<h1 id="创建线程的几种方法"><a href="#创建线程的几种方法" class="headerlink" title="创建线程的几种方法"></a>创建线程的几种方法</h1><h2 id="继承Thread类创建线程"><a href="#继承Thread类创建线程" class="headerlink" title="继承Thread类创建线程"></a>继承Thread类创建线程</h2><p>多线程的创建，方式一：继承于Thread类</p>
<ol>
<li>创建一个继承于Thread类的子类</li>
<li>重写Thread类的run() –&gt; 将此线程执行的操作声明在run()中</li>
<li>创建Thread类的子类的对象</li>
<li>通过此对象调用start()</li>
</ol>
<ul>
<li>例子：遍历100以内的所有的偶数</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//1. 创建一个继承于Thread类的子类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</span><br><span class="line">    <span class="comment">//2. 重写Thread类的run()</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(i % <span class="number">2</span> == <span class="number">0</span>)&#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;:&quot;</span> + i);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//3. 创建Thread类的子类的对象</span></span><br><span class="line">        MyThread t1 = <span class="keyword">new</span> MyThread();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//4.通过此对象调用start():①启动当前线程 ② 调用当前线程的run()</span></span><br><span class="line">        t1.start();</span><br><span class="line">        <span class="comment">//问题一：我们不能通过直接调用run()的方式启动线程。</span></span><br><span class="line"><span class="comment">//        t1.run();</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">        问题二：再启动一个线程，遍历100以内的偶数。不可以还让已经start()的线程去执行。</span></span><br><span class="line"><span class="comment">        会报IllegalThreadStateException</span></span><br><span class="line"><span class="comment">        */</span>    </span><br><span class="line"><span class="comment">//        t1.start();</span></span><br><span class="line">        <span class="comment">//我们需要重新创建一个线程的对象</span></span><br><span class="line">        MyThread t2 = <span class="keyword">new</span> MyThread();</span><br><span class="line">        t2.start();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//如下操作仍然是在main线程中执行的。</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(i % <span class="number">2</span> == <span class="number">0</span>)&#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;:&quot;</span> + i + <span class="string">&quot;***********main()************&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h2 id="实现Runnable接口创建线程"><a href="#实现Runnable接口创建线程" class="headerlink" title="实现Runnable接口创建线程"></a>实现Runnable接口创建线程</h2><p>1、创建多线程的方式二：实现Runnable接口</p>
<ol>
<li>创建一个实现了Runnable接口的类</li>
<li>实现类去实现Runnable中的抽象方法：run()</li>
<li>创建实现类的对象</li>
<li>将此对象作为参数传递到Thread类的构造器中，创建Thread类的对象</li>
<li>通过Thread类的对象调用start()</li>
</ol>
<p>2、 比较创建线程的两种方式。<br> 开发中：优先选择：实现Runnable接口的方式<br> 原因：实现的方式没有类的单继承性的局限性，实现的方式更适合来处理多个线程有共享数据的情况。 </p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//1. 创建一个实现了Runnable接口的类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MThread</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//2. 实现类去实现Runnable中的抽象方法：run()</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">100</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(i % <span class="number">2</span> == <span class="number">0</span>)&#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;:&quot;</span> + i);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadTest1</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//3. 创建实现类的对象</span></span><br><span class="line">        MThread mThread = <span class="keyword">new</span> MThread();</span><br><span class="line">        <span class="comment">//4. 将此对象作为参数传递到Thread类的构造器中，创建Thread类的对象</span></span><br><span class="line">        Thread t1 = <span class="keyword">new</span> Thread(mThread);</span><br><span class="line">        t1.setName(<span class="string">&quot;线程1&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">        5. 通过Thread类的对象调用start():① 启动线程 ②调用当前线程的run()--&gt;</span></span><br><span class="line"><span class="comment">        调用了Runnable类型的target的run()</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        t1.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//再启动一个线程，遍历100以内的偶数</span></span><br><span class="line">        Thread t2 = <span class="keyword">new</span> Thread(mThread);</span><br><span class="line">        t2.setName(<span class="string">&quot;线程2&quot;</span>);</span><br><span class="line">        t2.start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="Thread和Runnable的关系"><a href="#Thread和Runnable的关系" class="headerlink" title="Thread和Runnable的关系"></a>Thread和Runnable的关系</h2><p> 联系：public class Thread implements Runnable<br> 相同点：两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。</p>
<h3 id="Runnable接口构造线程源码"><a href="#Runnable接口构造线程源码" class="headerlink" title="Runnable接口构造线程源码"></a>Runnable接口构造线程源码</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*下面是Thread类的部分源码*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//1.用Runnable接口创建线程时会进入这个方法</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Thread</span><span class="params">(Runnable target)</span> </span>&#123;</span><br><span class="line">        init(<span class="keyword">null</span>, target, <span class="string">&quot;Thread-&quot;</span> + nextThreadNum(), <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//2.接着调用这个方法</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(ThreadGroup g, Runnable target, String name,</span></span></span><br><span class="line"><span class="function"><span class="params">                      <span class="keyword">long</span> stackSize)</span> </span>&#123;</span><br><span class="line">        init(g, target, name, stackSize, <span class="keyword">null</span>, <span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//3.再调用这个方法</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(ThreadGroup g, Runnable target, String name,</span></span></span><br><span class="line"><span class="function"><span class="params">                      <span class="keyword">long</span> stackSize, AccessControlContext acc,</span></span></span><br><span class="line"><span class="function"><span class="params">                      <span class="keyword">boolean</span> inheritThreadLocals)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (name == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException(<span class="string">&quot;name cannot be null&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line"></span><br><span class="line">        Thread parent = currentThread();</span><br><span class="line">        SecurityManager security = System.getSecurityManager();</span><br><span class="line">        <span class="keyword">if</span> (g == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">/* Determine if it&#x27;s an applet or not */</span></span><br><span class="line"></span><br><span class="line">            <span class="comment">/* If there is a security manager, ask the security manager</span></span><br><span class="line"><span class="comment">               what to do. */</span></span><br><span class="line">            <span class="keyword">if</span> (security != <span class="keyword">null</span>) &#123;</span><br><span class="line">                g = security.getThreadGroup();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/* If the security doesn&#x27;t have a strong opinion of the matter</span></span><br><span class="line"><span class="comment">               use the parent thread group. */</span></span><br><span class="line">            <span class="keyword">if</span> (g == <span class="keyword">null</span>) &#123;</span><br><span class="line">                g = parent.getThreadGroup();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* checkAccess regardless of whether or not threadgroup is</span></span><br><span class="line"><span class="comment">           explicitly passed in. */</span></span><br><span class="line">        g.checkAccess();</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * Do we have the required permissions?</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">if</span> (security != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (isCCLOverridden(getClass())) &#123;</span><br><span class="line">                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        g.addUnstarted();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">this</span>.group = g;</span><br><span class="line">        <span class="keyword">this</span>.daemon = parent.isDaemon();</span><br><span class="line">        <span class="keyword">this</span>.priority = parent.getPriority();</span><br><span class="line">        <span class="keyword">if</span> (security == <span class="keyword">null</span> || isCCLOverridden(parent.getClass()))</span><br><span class="line">            <span class="keyword">this</span>.contextClassLoader = parent.getContextClassLoader();</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="keyword">this</span>.contextClassLoader = parent.contextClassLoader;</span><br><span class="line">        <span class="keyword">this</span>.inheritedAccessControlContext =</span><br><span class="line">                acc != <span class="keyword">null</span> ? acc : AccessController.getContext();</span><br><span class="line">        <span class="comment">//4.最后在这里将Runnable接口(target)赋值给Thread自己的target成员属性     </span></span><br><span class="line">        <span class="keyword">this</span>.target = target;</span><br><span class="line">        setPriority(priority);</span><br><span class="line">        <span class="keyword">if</span> (inheritThreadLocals &amp;&amp; parent.inheritableThreadLocals != <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">this</span>.inheritableThreadLocals =</span><br><span class="line">                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);</span><br><span class="line">        <span class="comment">/* Stash the specified stack size in case the VM cares */</span></span><br><span class="line">        <span class="keyword">this</span>.stackSize = stackSize;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* Set thread ID */</span></span><br><span class="line">        tid = nextThreadID();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*如果你是实现了runnable接口，那么在上面的代码中target便不会为null，那么最终就会通过重写的</span></span><br><span class="line"><span class="comment">规则去调用真正实现了Runnable接口(你之前传进来的那个Runnable接口实现类)的类里的run方法*/</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    </span><br><span class="line">        <span class="keyword">if</span> (target != <span class="keyword">null</span>) &#123;</span><br><span class="line">            target.run();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>1、多线程的设计之中，使用了代理设计模式的结构，用户自定义的线程主体只是负责项目核心功能的实现，而所有的辅助实现全部交由Thread类来处理。<br>2、在进行Thread启动多线程的时候调用的是start()方法，而后找到的是run()方法，但通过Thread类的构造方法传递了一个Runnable接口对象的时候，那么该接口对象将被Thread类中的target属性所保存，在start()方法执行的时候会调用Thread类中的run()方法。而这个run()方法去调用实现了Runnable接口的那个类所重写过run()方法，进而执行相应的逻辑。多线程开发的本质实质上是在于多个线程可以进行同一资源的抢占，那么Thread主要描述的是线程，而资源的描述是通过Runnable完成的。如下图所示：</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0003.png">





<h3 id="Thread类构造线程源码"><a href="#Thread类构造线程源码" class="headerlink" title="Thread类构造线程源码"></a>Thread类构造线程源码</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">MyThread t2 = <span class="keyword">new</span> MyThread(); <span class="comment">//这个构造函数会默认调用Super();也就是Thread类的无参构造</span></span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//代码从上往下顺序执行</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Thread</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        init(<span class="keyword">null</span>, <span class="keyword">null</span>, <span class="string">&quot;Thread-&quot;</span> + nextThreadNum(), <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(ThreadGroup g, Runnable target, String name,</span></span></span><br><span class="line"><span class="function"><span class="params">                      <span class="keyword">long</span> stackSize)</span> </span>&#123;</span><br><span class="line">        init(g, target, name, stackSize, <span class="keyword">null</span>, <span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(ThreadGroup g, Runnable target, String name,</span></span></span><br><span class="line"><span class="function"><span class="params">                      <span class="keyword">long</span> stackSize, AccessControlContext acc,</span></span></span><br><span class="line"><span class="function"><span class="params">                      <span class="keyword">boolean</span> inheritThreadLocals)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (name == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException(<span class="string">&quot;name cannot be null&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line"></span><br><span class="line">        Thread parent = currentThread();</span><br><span class="line">        SecurityManager security = System.getSecurityManager();</span><br><span class="line">        <span class="keyword">if</span> (g == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">/* Determine if it&#x27;s an applet or not */</span></span><br><span class="line"></span><br><span class="line">            <span class="comment">/* If there is a security manager, ask the security manager</span></span><br><span class="line"><span class="comment">               what to do. */</span></span><br><span class="line">            <span class="keyword">if</span> (security != <span class="keyword">null</span>) &#123;</span><br><span class="line">                g = security.getThreadGroup();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/* If the security doesn&#x27;t have a strong opinion of the matter</span></span><br><span class="line"><span class="comment">               use the parent thread group. */</span></span><br><span class="line">            <span class="keyword">if</span> (g == <span class="keyword">null</span>) &#123;</span><br><span class="line">                g = parent.getThreadGroup();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* checkAccess regardless of whether or not threadgroup is</span></span><br><span class="line"><span class="comment">           explicitly passed in. */</span></span><br><span class="line">        g.checkAccess();</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">         * Do we have the required permissions?</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">if</span> (security != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (isCCLOverridden(getClass())) &#123;</span><br><span class="line">                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        g.addUnstarted();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">this</span>.group = g;</span><br><span class="line">        <span class="keyword">this</span>.daemon = parent.isDaemon();</span><br><span class="line">        <span class="keyword">this</span>.priority = parent.getPriority();</span><br><span class="line">        <span class="keyword">if</span> (security == <span class="keyword">null</span> || isCCLOverridden(parent.getClass()))</span><br><span class="line">            <span class="keyword">this</span>.contextClassLoader = parent.getContextClassLoader();</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="keyword">this</span>.contextClassLoader = parent.contextClassLoader;</span><br><span class="line">        <span class="keyword">this</span>.inheritedAccessControlContext =</span><br><span class="line">                acc != <span class="keyword">null</span> ? acc : AccessController.getContext();</span><br><span class="line">        <span class="keyword">this</span>.target = target;</span><br><span class="line">        setPriority(priority);</span><br><span class="line">        <span class="keyword">if</span> (inheritThreadLocals &amp;&amp; parent.inheritableThreadLocals != <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">this</span>.inheritableThreadLocals =</span><br><span class="line">                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);</span><br><span class="line">        <span class="comment">/* Stash the specified stack size in case the VM cares */</span></span><br><span class="line">        <span class="keyword">this</span>.stackSize = stackSize;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* Set thread ID */</span></span><br><span class="line">        tid = nextThreadID();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*由于这里是通过继承Thread类来实现的线程，那么target这个东西就是Null。但是因为你继承</span></span><br><span class="line"><span class="comment">了Runnable接口并且重写了run()，所以最终还是调用子类的run()*/</span></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (target != <span class="keyword">null</span>) &#123;</span><br><span class="line">            target.run();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>



<h3 id="最直观的代码描述"><a href="#最直观的代码描述" class="headerlink" title="最直观的代码描述"></a>最直观的代码描述</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Window</span> <span class="keyword">extends</span> <span class="title">Thread</span></span>&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span>  <span class="keyword">int</span> ticket = <span class="number">100</span>;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span>(<span class="keyword">true</span>)&#123;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>(ticket &gt; <span class="number">0</span>)&#123;</span><br><span class="line">                System.out.println(getName() + <span class="string">&quot;：卖票，票号为：&quot;</span> + ticket);</span><br><span class="line">                ticket--;</span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WindowTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Window t1 = <span class="keyword">new</span> Window();</span><br><span class="line">        Window t2 = <span class="keyword">new</span> Window();</span><br><span class="line">        Window t3 = <span class="keyword">new</span> Window();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        t1.setName(<span class="string">&quot;窗口1&quot;</span>);</span><br><span class="line">        t2.setName(<span class="string">&quot;窗口2&quot;</span>);</span><br><span class="line">        t3.setName(<span class="string">&quot;窗口3&quot;</span>);</span><br><span class="line"></span><br><span class="line">        t1.start();</span><br><span class="line">        t2.start();</span><br><span class="line">        t3.start();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Window1</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> ticket = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="keyword">true</span>)&#123;</span><br><span class="line">            <span class="keyword">if</span>(ticket &gt; <span class="number">0</span>)&#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;:卖票，票号为：&quot;</span> + ticket);</span><br><span class="line">                ticket--;</span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">WindowTest1</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Window1 w = <span class="keyword">new</span> Window1();</span><br><span class="line"></span><br><span class="line">        Thread t1 = <span class="keyword">new</span> Thread(w);</span><br><span class="line">        Thread t2 = <span class="keyword">new</span> Thread(w);</span><br><span class="line">        Thread t3 = <span class="keyword">new</span> Thread(w);</span><br><span class="line"></span><br><span class="line">        t1.setName(<span class="string">&quot;窗口1&quot;</span>);</span><br><span class="line">        t2.setName(<span class="string">&quot;窗口2&quot;</span>);</span><br><span class="line">        t3.setName(<span class="string">&quot;窗口3&quot;</span>);</span><br><span class="line"></span><br><span class="line">        t1.start();</span><br><span class="line">        t2.start();</span><br><span class="line">        t3.start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>1、继承Thread类的方式，new了三个Thread，实际上是有300张票。</p>
<p>2、实现Runnable接口的方式，new了三个Thread，实际上是有100张票。</p>
<p>3、也就是说实现Runnable接口的线程中，成员属性是所有线程共有的。但是继承Thread类的线程中，成员属性是各个线程独有的，其它线程看不到，除非采用static的方式才能使各个线程都能看到。</p>
<p>4、就像上面说的Runnable相当于资源，Thread才是线程。用Runnable创建线程时，new了多个Thread，但是传进去的参数都是同一个Runnable（资源）。用Thread创建线程时，就直接new了多个线程，每个线程都有自己的Runnable（资源）。在Thread源码中就是用target变量（这是一个Runnable类型的变量）来表示这个资源。</p>
<p>5、同时因为这两个的区别，在并发编程中，继承了Thread的子类在进行线程同步时不能将成员变量当做锁，因为多个线程拿到的不是同一把锁，不过用static变量可以解决这个问题。而实现了Runnable接口的类在进行线程同步时没有这个问题。</p>
<h2 id="实现Callable接口创建线程"><a href="#实现Callable接口创建线程" class="headerlink" title="实现Callable接口创建线程"></a>实现Callable接口创建线程</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//Callable实现多线程</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyThread</span> <span class="keyword">implements</span> <span class="title">Callable</span>&lt;<span class="title">String</span>&gt; </span>&#123;<span class="comment">//线程的主体类</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> x = <span class="number">0</span>; x &lt; <span class="number">10</span>; x++) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;*******线程执行，x=&quot;</span> + x + <span class="string">&quot;********&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;线程执行完毕&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Demo1</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        FutureTask&lt;String&gt; task = <span class="keyword">new</span> FutureTask&lt;&gt;(<span class="keyword">new</span> MyThread());</span><br><span class="line">        <span class="keyword">new</span> Thread(task).start();</span><br><span class="line">        System.out.println(<span class="string">&quot;线程返回数据&quot;</span> + task.get());</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Callable最主要的就是提供带有返回值的call方法来创建线程。不过Callable要和Future实现类连着用，关于Future的一系列知识会在后面几个系列讲到。</p>
<h1 id="策略模式在Thread和Runnable中的应用"><a href="#策略模式在Thread和Runnable中的应用" class="headerlink" title="策略模式在Thread和Runnable中的应用"></a>策略模式在Thread和Runnable中的应用</h1><p>Runnable接口最重要的方法—–run方法，使用了<strong>策略者模式</strong>将执行的逻辑(run方法)和程序的执行单元(start0方法)分离出来，使用户可以定义自己的程序处理逻辑，更符合面向对象的思想。</p>
<h1 id="Thread的构造方法"><a href="#Thread的构造方法" class="headerlink" title="Thread的构造方法"></a>Thread的构造方法</h1><ul>
<li><p>创建线程对象Thread，<code>默认有一个线程名，以Thread-开头，从0开始计数</code>，如“Thread-0、Thread-1、Thread-2 …”</p>
</li>
<li><p>如果没有传递Runnable或者没有覆写Thread的run方法，<code>该Thread不会调用任何方法</code></p>
</li>
<li><p>如果传递Runnable接口的实例或者覆写run方法，则<code>会执行该方法的逻辑单元</code>（逻辑代码）</p>
</li>
<li><p>如果构造线程对象时，未传入ThreadGroup，<code>Thread会默认获取父线程的ThreadGroup作为该线程的ThreadGroup</code>，此时子线程和父线程会在同一个ThreadGroup中</p>
</li>
<li><p>stackSize可以<code>提高线程栈的深度</code>，放更多栈帧，但是会<code>减少能创建的线程数目</code></p>
</li>
<li><p>stackSize默认是0，<code>如果是0，代表着被忽略，该参数会被JNI函数调用</code>，但是注意某些平台可能会失效，<code>可以通过“-Xss10m”设置</code></p>
</li>
</ul>
<p>具体的介绍可以看Java的API文档</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*下面是Thread 的部分源码*/</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Thread</span><span class="params">(Runnable target)</span> </span>&#123;</span><br><span class="line">    init(<span class="keyword">null</span>, target, <span class="string">&quot;Thread-&quot;</span> + nextThreadNum(), <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Thread</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">    init(<span class="keyword">null</span>, <span class="keyword">null</span>, name, <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line">		↓ ↓	↓	</span><br><span class="line">         ↓ ↓	</span><br><span class="line">          ↓	</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(ThreadGroup g, Runnable target, String name,</span></span></span><br><span class="line"><span class="function"><span class="params">                      <span class="keyword">long</span> stackSize)</span> </span>&#123;</span><br><span class="line">    init(g, target, name, stackSize, <span class="keyword">null</span>, <span class="keyword">true</span>);</span><br><span class="line">&#125;</span><br><span class="line">		↓ ↓	↓	</span><br><span class="line">         ↓ ↓	</span><br><span class="line">          ↓	</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">(ThreadGroup g, Runnable target, String name,</span></span></span><br><span class="line"><span class="function"><span class="params">                      <span class="keyword">long</span> stackSize, AccessControlContext acc,</span></span></span><br><span class="line"><span class="function"><span class="params">                      <span class="keyword">boolean</span> inheritThreadLocals)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//中间源码省略</span></span><br><span class="line">    <span class="keyword">this</span>.target = target;<span class="comment">//①</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/* What will be run. */</span></span><br><span class="line"><span class="keyword">private</span> Runnable target; <span class="comment">//Thread类中的target属性</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (target != <span class="keyword">null</span>) &#123; <span class="comment">//②</span></span><br><span class="line">        target.run();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<blockquote>
<p>源码标记解读：</p>
<p>1、如果Thread类的构造方法传递了一个Runnable接口对象</p>
<p>①那么该接口对象将被Thread类中的target属性所保存。</p>
<p>②在start()方法执行的时候会调用Thread类中的run()方法。因为target不为null， target.run()就去调用实现Runnable接口的子类重写的run()。</p>
<p>2、如果Thread类的构造方传没传Runnable接口对象</p>
<p>①Thread类中的target属性保存的就是null。</p>
<p>②在start()方法执行的时候会调用Thread类中的run()方法。因为target为null，只能去调用继承Thread的子类所重写的run()。</p>
</blockquote>
<p>JVM一旦启动，虚拟机栈的大小已经确定了。但是如果你创建Thread的时候传了stackSize（该线程占用的stack大小），该参数会被JNI函数去使用。如果没传这个参数，就默认为0，表示忽略这个参数。注：stackSize在有一些平台上是无效的。</p>
<h1 id="start-源码"><a href="#start-源码" class="headerlink" title="start()源码"></a>start()源码</h1><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">start</span><span class="params">()</span> </span>&#123;</span><br><span class="line">   </span><br><span class="line">    <span class="keyword">if</span> (threadStatus != <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalThreadStateException();<span class="comment">//①</span></span><br><span class="line"></span><br><span class="line">   </span><br><span class="line">    group.add(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">boolean</span> started = <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        start0();</span><br><span class="line">        started = <span class="keyword">true</span>;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (!started) &#123;</span><br><span class="line">                group.threadStartFailed(<span class="keyword">this</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Throwable ignore) &#123;</span><br><span class="line">            <span class="comment">/* do nothing. If start0 threw a Throwable then</span></span><br><span class="line"><span class="comment">              it will be passed up the call stack */</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">start0</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (target != <span class="keyword">null</span>) &#123;</span><br><span class="line">        target.run();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>源码标记解读：</p>
<p>①当多次调用start()，会抛出throw new IllegalThreadStateException()异常。也就是每一个线程类的对象只允许启动一次，如果重复启动则就抛出此异常。</p>
</blockquote>
<h2 id="为什么线程的启动不直接使用run-而必须使用start-呢"><a href="#为什么线程的启动不直接使用run-而必须使用start-呢" class="headerlink" title="为什么线程的启动不直接使用run()而必须使用start()呢?"></a>为什么线程的启动不直接使用run()而必须使用start()呢?</h2><p>1、如果直接调用run()方法，相当于就是简单的调用一个普通方法。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0004.png">

<p>2、run()的调用是在start0()这个Native C++方法里调用的</p>
<h1 id="线程生命周期"><a href="#线程生命周期" class="headerlink" title="线程生命周期"></a>线程生命周期</h1><p>Java 线程在运行的生命周期中的指定时刻只可能处于下面 6 种不同状态的其中一个状态，这几个状态在Java源码中用枚举来表示。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0005.png">

<p>线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。Java 线程状态变迁如下图所示。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0006.png">

<blockquote>
<p> 图中 wait到 runnable状态的转换中，<code>join</code>实际上是<code>Thread</code>类的方法，但这里写成了<code>Object</code>。</p>
</blockquote>
<p>1、由上图可以看出：线程创建之后它将处于 <strong>NEW（新建）</strong> 状态，调用 <code>start()</code> 方法后开始运行，线程这时候处于 <strong>READY（可运行）</strong> 状态。可运行状态的线程获得了 CPU 时间片（timeslice）后就处于 <strong>RUNNING（运行）</strong> 状态。</p>
<p>2、操作系统隐藏 Java 虚拟机（JVM）中的 READY 和 RUNNING 状态，它只能看到 RUNNABLE 状态，所以 Java 系统一般将这两个状态统称为 <strong>RUNNABLE（运行中）</strong> 状态 。</p>
<p>3、调用sleep()方法，会进入Blocked状态。sleep()结束之后，Blocked状态首先回到的是Runnable状态中的Ready（也就是可运行状态，但并未运行）。只有拿到了cpu的时间片才会进入Runnable中的Running状态。</p>
<h1 id="Thread常用API"><a href="#Thread常用API" class="headerlink" title="Thread常用API"></a>Thread常用API</h1><ul>
<li>获取当前存活的线程数：<code>public int activeCount()</code></li>
<li>获取当前线程组的线程的集合：<code>public int enumerate(Thread[] list)</code></li>
</ul>
<h1 id="一个Java程序有哪些线程？"><a href="#一个Java程序有哪些线程？" class="headerlink" title="一个Java程序有哪些线程？"></a>一个Java程序有哪些线程？</h1><p>1、当你调用一个线程start()方法的时候，此时至少有两个线程，一个是调用你的线程，还有一个是被你创建出来的线程。</p>
<p>例子：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    Thread t1 = <span class="keyword">new</span> Thread() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;==========&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    t1.start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里面就是一个调用你的线程（main线程），一个被你创建出来的线程（t1，名字可能是Thread-0）</p>
<p>2、当JVM启动后，实际有多个线程，但是至少有一个非守护线程（比如main线程）。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0007.png">

<ul>
<li><p>Finalizer：GC守护线程</p>
</li>
<li><p>RMI：Java自带的远程方法调用（秋招面试，有个面试官问过）</p>
</li>
<li><p>Monitor ：是一个守护线程，负责监听一些操作，也在main线程组中</p>
</li>
<li><p>其它：我用的是IDEA，其它的应该是IDEA的线程，比如鼠标监听啥的。</p>
</li>
</ul>
<h1 id="守护线程"><a href="#守护线程" class="headerlink" title="守护线程"></a>守护线程</h1><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line"></span><br><span class="line">    Thread t = <span class="keyword">new</span> Thread() &#123;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot; running&quot;</span>);</span><br><span class="line">                Thread.sleep(<span class="number">100000</span>);<span class="comment">//①</span></span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot; done.&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;; <span class="comment">//new</span></span><br><span class="line">    </span><br><span class="line">    t.setDaemon(<span class="keyword">true</span>);<span class="comment">//②</span></span><br><span class="line">	t.start();</span><br><span class="line">    Thread.sleep(<span class="number">5_000</span>);   <span class="comment">//JDK1.7</span></span><br><span class="line">    System.out.println(Thread.currentThread().getName());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>源码标记解读：</p>
<p>①变量名为t的线程Thread-0，睡眠100秒。</p>
<p>②但是在主函数里Thread-0设置成了main线程的守护线程。所以5秒之后main线程结束了，即使在①这里守护线程还是处于睡眠100秒的状态，但由于他是守护线程，非守护线程main结束了，守护线程也必须结束。</p>
<p>1、但是如果Thread-0线程不是守护线程，即使main线程结束了，Thread-0线程仍然会睡眠100秒再结束。</p>
<ul>
<li>当主线程死亡后，守护线程会跟着死亡</li>
<li>可以帮助做一些辅助性的东西，如“心跳检测”</li>
<li>设置守护线程：<code>public final void setDaemon(boolean on)</code></li>
</ul>
</blockquote>
<h2 id="用处"><a href="#用处" class="headerlink" title="用处"></a>用处</h2><p>A和B之间有一条网络连接，可以用守护线程来进行发送心跳，一旦A和B连接断开，非守护线程就结束了，守护线程（也就是心跳没有必要再发送了）也刚好断开。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    Thread t = <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">        Thread innerThread = <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;Do some thing for health check.&quot;</span>);</span><br><span class="line">                    Thread.sleep(<span class="number">1_000</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">      <span class="comment">//  innerThread.setDaemon(true);</span></span><br><span class="line">        innerThread.start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">1_000</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;T thread finish done.&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">//t.setDaemon(true);</span></span><br><span class="line">    t.start();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">设置该线程为守护线程必须在启动它之前。如果t.start()之后，再t.setDaemon(true);</span></span><br><span class="line"><span class="comment">会抛出IllegalThreadStateException</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>输出结果：</p>
<p>Do some thing for health check.<br>Do some thing for health check.<br>T thread finish done.          //此时main线程已经结束，但是由于innerThread还在发送心跳，应用不会关闭<br>Do some thing for health check.<br>Do some thing for health check.<br>Do some thing for health check.<br>Do some thing for health check.</p>
</blockquote>
<blockquote>
<p>守护线程还有其它很多用处，在后面的文章里还会有出现。</p>
</blockquote>
<h1 id="join方法"><a href="#join方法" class="headerlink" title="join方法"></a>join方法</h1><p><strong>例子1</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    Thread t1 = <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">        IntStream.range(<span class="number">1</span>, <span class="number">1000</span>)</span><br><span class="line">                .forEach(i -&gt; System.out.println(Thread.currentThread().getName() + <span class="string">&quot;-&gt;&quot;</span> + i));</span><br><span class="line">    &#125;);</span><br><span class="line">    Thread t2 = <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">        IntStream.range(<span class="number">1</span>, <span class="number">1000</span>)</span><br><span class="line">                .forEach(i -&gt; System.out.println(Thread.currentThread().getName() + <span class="string">&quot;-&gt;&quot;</span> + i));</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    t1.start();</span><br><span class="line">    t2.start();</span><br><span class="line">    t1.join();</span><br><span class="line">    t2.join();</span><br><span class="line"></span><br><span class="line">    Optional.of(<span class="string">&quot;All of tasks finish done.&quot;</span>).ifPresent(System.out::println);</span><br><span class="line">    IntStream.range(<span class="number">1</span>, <span class="number">1000</span>)</span><br><span class="line">            .forEach(i -&gt; System.out.println(Thread.currentThread().getName() + <span class="string">&quot;-&gt;&quot;</span> + i));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>默认传入的数字为0，这里是在main线程里调用了两个线程的join()，所以main线程会等到Thread-0和Thread-1线程执行完再执行它自己。</li>
<li>join必须在start方法之后，并且join()是对wait()的封装。（源码中可以清楚的看到）</li>
</ul>
<ul>
<li>也就是说，t.join()方法阻塞调用此方法的线程(calling thread)进入 TIMED_WAITING或WAITING 状态。直到线程t完成，此线程再继续。</li>
<li>join也有人理解成插队，比如在main线程中调用t.join()，就是t线程要插main线程的队，main线程要去等待。</li>
</ul>
<p><strong>例子2</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">        Thread t1 = <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            IntStream.range(<span class="number">1</span>, <span class="number">1000</span>)</span><br><span class="line">                    .forEach(i -&gt; System.out.println(Thread.currentThread().getName() + <span class="string">&quot;-&gt;&quot;</span> + i));</span><br><span class="line">        &#125;);</span><br><span class="line">        Thread t2 = <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                t1.join();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            IntStream.range(<span class="number">1</span>, <span class="number">1000</span>)</span><br><span class="line">                    .forEach(i -&gt; System.out.println(Thread.currentThread().getName() + <span class="string">&quot;-&gt;&quot;</span> + i));</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        t1.start();</span><br><span class="line">        t2.start();</span><br><span class="line"><span class="comment">//        t1.join();</span></span><br><span class="line">        t2.join();</span><br><span class="line"></span><br><span class="line">        Optional.of(<span class="string">&quot;All of tasks finish done.&quot;</span>).ifPresent(System.out::println);</span><br><span class="line">        IntStream.range(<span class="number">1</span>, <span class="number">1000</span>)</span><br><span class="line">                .forEach(i -&gt; System.out.println(Thread.currentThread().getName() + <span class="string">&quot;-&gt;&quot;</span> + i));</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>这里是在t2（<span style="color:red;font-weight:bold">我们以后就都用变量名来称呼线程了</span>）线程了。t1.join()了。所以t2线程会等待t1线程打印完，t2自己才会打印。然后t2.join()，main线程也要等待t2线程。总体执行顺序就是t1–&gt;t2–&gt;main</li>
<li>通过上方例子可以用join实现类似于CompletableFuture的异步任务编排。（后面会讲）</li>
</ul>
<h1 id="中断"><a href="#中断" class="headerlink" title="中断"></a>中断</h1><p>1、Java 中的中断和操作系统的中断还不一样，这里就按照<strong>状态</strong>来理解吧，不要和操作系统的中断联系在一起</p>
<p>2、记住中断只是一个状态，Java的方法可以选择对这个中断进行响应，也可以选择不响应。响应的意思就是写相对应的代码执行相对应的操作，不响应的意思就是什么代码都不写。</p>
<h2 id="几个方法"><a href="#几个方法" class="headerlink" title="几个方法"></a>几个方法</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Thread 类中的实例方法，持有线程实例引用即可检测线程中断状态</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isInterrupted</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">1、Thread 中的静态方法，检测调用这个方法的线程是否已经中断</span></span><br><span class="line"><span class="comment">2、注意：这个方法返回中断状态的同时，会将此线程的中断状态重置为 false</span></span><br><span class="line"><span class="comment">如果我们连续调用两次这个方法的话，第二次的返回值肯定就是 false 了</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">interrupted</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Thread 类中的实例方法，用于设置一个线程的中断状态为 true</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">interrupt</span><span class="params">()</span> </span>&#123;&#125;</span><br></pre></td></tr></table></figure>





<h2 id="小tip"><a href="#小tip" class="headerlink" title="小tip"></a>小tip</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">interrupted</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isInterrupted</span><span class="params">()</span><span class="comment">//这个会清除中断状态</span></span></span><br><span class="line"><span class="function"></span></span><br></pre></td></tr></table></figure>

<p>为什么要这么设置呢？原因在于：</p>
<ul>
<li>interrupted()是一个静态方法，可以在Runnable接口实例中使用</li>
<li>isInterrupted()是一个Thread的实例方法，在重写Thread的run方法时使用</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadInterrupt</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">        Thread t1 = <span class="keyword">new</span> Thread(() -&gt; &#123;</span><br><span class="line">            System.out.println(Thread.interrupted());</span><br><span class="line">        &#125;);  <span class="comment">//这个new Thread用的是runnable接口那个构造函数</span></span><br><span class="line"></span><br><span class="line">        Thread t2 = <span class="keyword">new</span> Thread()&#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                System.out.println(isInterrupted());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;<span class="comment">//这个new Thread用的就是Thread的空参构造</span></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>也就是说接口中不能调用Thread的实例方法，只能通过静态方法来判断是否发生中断</p>
<h2 id="重难点"><a href="#重难点" class="headerlink" title="重难点"></a>重难点</h2><p>当然，中断除了是线程状态外，还有其他含义，否则也不需要专门搞一个这个概念出来了。</p>
<blockquote>
<p>初学者肯定以为 thread.interrupt() 方法是用来暂停线程的，主要是和它对应中文翻译的“中断”有关。中断在并发中是常用的手段，请大家一定好好掌握。可以将中断理解为线程的状态，它的特殊之处在于设置了中断状态为 true 后，这几个方法会感知到：</p>
<ol>
<li><p>wait(), wait(long), wait(long, int), join(), join(long), join(long, int), sleep(long), sleep(long, int)</p>
<p>这些方法都有一个共同之处，方法签名上都有<code>throws InterruptedException</code>，这个就是用来响应中断状态修改的。</p>
</li>
<li><p>如果线程阻塞在 InterruptibleChannel 类的 IO 操作中，那么这个 channel 会被关闭。</p>
</li>
<li><p>如果线程阻塞在一个 Selector 中，那么 select 方法会立即返回。</p>
</li>
</ol>
<p>对于以上 3 种情况是最特殊的，因为他们能自动感知到中断（这里说自动，当然也是基于底层实现），<strong>并且在做出相应的操作后都会重置中断状态为 false</strong>。然后执行相应的操作（通常就是跳到 catch 异常处）。</p>
<p>如果不是以上3种情况，那么，线程的 interrupt() 方法被调用，会将线程的中断状态设置为 true。</p>
<p>那是不是只有以上 3 种方法能自动感知到中断呢？不是的，如果线程阻塞在 LockSupport.park(Object obj) 方法，也叫挂起，这个时候的中断也会导致线程唤醒，但是唤醒后不会重置中断状态，所以唤醒后去检测中断状态将是 true。</p>
</blockquote>
<blockquote>
<p>资料:  <a target="_blank" rel="noopener" href="https://docs.oracle.com/javase/specs/index.html">Oracle官方文档</a>    —&gt;   <a target="_blank" rel="noopener" href="https://docs.oracle.com/javase/specs/jls/se8/html/index.html"> The Java® Language Specification Java SE 8 Edition</a>   —&gt;   第17章 Threads and Locks</p>
</blockquote>
<h2 id="InterruptedException"><a href="#InterruptedException" class="headerlink" title="InterruptedException"></a>InterruptedException</h2><p>它是一个特殊的异常，不是说 JVM 对其有特殊的处理，而是它的使用场景比较特殊。通常，我们可以看到，像 Object 中的 wait() 方法，ReentrantLock 中的 lockInterruptibly() 方法，Thread 中的 sleep() 方法等等，这些方法都带有 <code>throws InterruptedException</code>，我们通常称这些方法为阻塞方法（blocking method）。</p>
<p>阻塞方法一个很明显的特征是，它们需要花费比较长的时间（不是绝对的，只是说明时间不可控），还有它们的方法结束返回往往依赖于外部条件，如 wait 方法依赖于其他线程的 notify，lock 方法依赖于其他线程的 unlock等等。</p>
<p>当我们看到方法上带有 <code>throws InterruptedException</code> 时，我们就要知道，这个方法应该是阻塞方法，我们如果希望它能早点返回的话，我们往往可以通过中断来实现。 </p>
<p>除了几个特殊类（如 Object，Thread等）外，感知中断并提前返回是通过轮询中断状态来实现的。我们自己需要写可中断的方法的时候，就是通过在合适的时机（通常在循环的开始处）去判断线程的中断状态，然后做相应的操作（通常是方法直接返回或者抛出异常）。当然，我们也要看到，如果我们一次循环花的时间比较长的话，那么就需要比较长的时间才能<strong>感知</strong>到线程中断了。</p>
<h2 id="wait-中断测试"><a href="#wait-中断测试" class="headerlink" title="wait()中断测试"></a>wait()中断测试</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    Thread t = <span class="keyword">new</span> Thread() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">                <span class="keyword">synchronized</span> (MONITOR) &#123;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        MONITOR.wait(<span class="number">10</span>);</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                        System.out.println(<span class="string">&quot;wait响应中断&quot;</span>);<span class="comment">//pos_1</span></span><br><span class="line">                        e.printStackTrace();<span class="comment">//pos_2</span></span><br><span class="line">                        System.out.println(isInterrupted());<span class="comment">//pos_3</span></span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line"></span><br><span class="line">    t.start();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        Thread.sleep(<span class="number">100</span>);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;sleep响应中断&quot;</span>);</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    System.out.println(t.isInterrupted());<span class="comment">//pos_4</span></span><br><span class="line">    t.interrupt();</span><br><span class="line">    System.out.println(t.isInterrupted());<span class="comment">//pos_5</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>注释掉e.printStackTrace();的输出</p>
<p>false                        //pos_4<br>true                         //pos_5<br>wait响应中断            //pos_1<br>false                         //pos_3        </p>
</blockquote>
<h2 id="join中断测试"><a href="#join中断测试" class="headerlink" title="join中断测试"></a>join中断测试</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Thread main = Thread.currentThread();</span><br><span class="line">Thread t2 = <span class="keyword">new</span> Thread() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">100</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        main.interrupt();  <span class="comment">//pos_1</span></span><br><span class="line">        System.out.println(<span class="string">&quot;interrupt&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">t2.start();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    t.join();  <span class="comment">//pos_2</span></span><br><span class="line">&#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">    e.printStackTrace();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>1、pos_2这里join的是main线程，所以pos_1这里需要中断main线程，才能收到中断信息。</p>
<h1 id="关闭线程"><a href="#关闭线程" class="headerlink" title="关闭线程"></a>关闭线程</h1><h2 id="优雅的关闭-通过一个Boolean"><a href="#优雅的关闭-通过一个Boolean" class="headerlink" title="优雅的关闭(通过一个Boolean)"></a>优雅的关闭(通过一个Boolean)</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Worker</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">boolean</span> start = <span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">while</span> (start) &#123;</span><br><span class="line">           <span class="comment">//执行相应的工作</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">shutdown</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.start = <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    Worker worker = <span class="keyword">new</span> Worker();</span><br><span class="line">    worker.start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        Thread.sleep(<span class="number">10000</span>);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    worker.shutdown();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="通过判断中断状态"><a href="#通过判断中断状态" class="headerlink" title="通过判断中断状态"></a>通过判断中断状态</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Worker</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (Thread.interrupted())&#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//pos_1    </span></span><br><span class="line">        &#125;</span><br><span class="line">    </span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    Worker worker = <span class="keyword">new</span> Worker();</span><br><span class="line">    worker.start();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        Thread.sleep(<span class="number">3000</span>);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    worker.interrupt();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>1、但是如果pos_1位置有一个很费时的IO操作，就没有机会执行到if判断那里，也就不能关闭线程。所以就需要下面的暴力方法</p>
<h2 id="暴力关闭（守护线程）"><a href="#暴力关闭（守护线程）" class="headerlink" title="暴力关闭（守护线程）"></a>暴力关闭（守护线程）</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadService</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//执行线程</span></span><br><span class="line">    <span class="keyword">private</span> Thread executeThread;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">boolean</span> finished = <span class="keyword">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Runnable task)</span> </span>&#123;</span><br><span class="line">        executeThread = <span class="keyword">new</span> Thread() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                Thread runner = <span class="keyword">new</span> Thread(task);</span><br><span class="line">                runner.setDaemon(<span class="keyword">true</span>);<span class="comment">//创建一个守护线程，让守护线程来执行工作</span></span><br><span class="line"></span><br><span class="line">                runner.start();</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 1、要让executeThread等守护线程执行完，才能执行executeThread自己的逻辑。不然守护线程</span></span><br><span class="line"><span class="comment"> * 可能就来不及执行真正的工作就死了。所以这里要join</span></span><br><span class="line"><span class="comment"> * 2、runner.join()，所以实际上等待的是executeThread       //pos_1</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">                    runner.join();</span><br><span class="line">                    finished = <span class="keyword">true</span>;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    <span class="comment">//e.printStackTrace();</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        executeThread.start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">shutdown</span><span class="params">(<span class="keyword">long</span> mills)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> currentTime = System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">while</span> (!finished) &#123;</span><br><span class="line">            <span class="keyword">if</span> ((System.currentTimeMillis() - currentTime) &gt;= mills) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;任务超时，需要结束他!&quot;</span>);</span><br><span class="line">                <span class="comment">/*</span></span><br><span class="line"><span class="comment">                 * pos_1那里，由于实际等待的是executeThread，所以这里中断executeThread。</span></span><br><span class="line"><span class="comment">                 * pos_1就可以捕获到中断，执行线程(executeThread)就结束了，进而真正执行任务的</span></span><br><span class="line"><span class="comment">                 * 守护线程runner也结束了</span></span><br><span class="line"><span class="comment">                 */</span></span><br><span class="line">                executeThread.interrupt();</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                executeThread.sleep(<span class="number">1</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;执行线程被打断!&quot;</span>);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        finished = <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadCloseForce</span> </span>&#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        ThreadService service = <span class="keyword">new</span> ThreadService();</span><br><span class="line">        <span class="keyword">long</span> start = System.currentTimeMillis();</span><br><span class="line">        service.execute(() -&gt; &#123;</span><br><span class="line">            <span class="comment">//load a very heavy resource. 模拟任务超时</span></span><br><span class="line">            <span class="comment">/*while (true) &#123;</span></span><br><span class="line"><span class="comment">			</span></span><br><span class="line"><span class="comment">            &#125;*/</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(<span class="number">5000</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        service.shutdown(<span class="number">10000</span>);</span><br><span class="line">        <span class="keyword">long</span> end = System.currentTimeMillis();</span><br><span class="line">        System.out.println(end - start);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>使用场景</strong>：分布式文件拷贝，如果拷贝的时间过长，则关闭该程序，防止程序一直阻塞。或者其他执行耗时很长的任务</p>
<p>守护线程的应用场景有很多</p>
<h1 id="并发编程中的三个问题"><a href="#并发编程中的三个问题" class="headerlink" title="并发编程中的三个问题"></a>并发编程中的三个问题</h1><h2 id="可见性"><a href="#可见性" class="headerlink" title="可见性"></a>可见性</h2><h3 id="可见性概念"><a href="#可见性概念" class="headerlink" title="可见性概念"></a>可见性概念</h3><p>可见性（Visibility）：是指一个线程对共享变量进行修改，另一个先立即得到修改后的新值。</p>
<h3 id="可见性演示"><a href="#可见性演示" class="headerlink" title="可见性演示"></a>可见性演示</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* 笔记</span></span><br><span class="line"><span class="comment"> * 1.当没有加Volatile的时候,while循环会一直在里面循环转圈</span></span><br><span class="line"><span class="comment"> * 2.当加了之后Volatile,由于可见性,一旦num改了之后,就会通知其他线程</span></span><br><span class="line"><span class="comment"> * 3.还有注意的时候不能用if,if不会重新拉回来再判断一次。(也叫做虚假唤醒)</span></span><br><span class="line"><span class="comment"> * 4.案例演示:一个线程对共享变量的修改,另一个线程不能立即得到新值</span></span><br><span class="line"><span class="comment"> * */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Video04_01</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        MyData myData = <span class="keyword">new</span> MyData();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt;&#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t come in &quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                TimeUnit.SECONDS.sleep(<span class="number">3</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//睡3秒之后再修改num,防止A线程先修改了num,那么到while循环的时候就会直接跳出去了</span></span><br><span class="line">            myData.addTo60();</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t come out&quot;</span>);</span><br><span class="line">        &#125;,<span class="string">&quot;A&quot;</span>).start();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span>(myData.num == <span class="number">0</span>)&#123;</span><br><span class="line">            <span class="comment">//只有当num不等于0的时候,才会跳出循环</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyData</span></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addTo60</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.num = <span class="number">60</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>由上面代码可以看出，并发编程时，会出现可见性问题，当一个线程对共享变量进行了修改，另外的线程并没有立即看到修改后的最新值。</p>
<h2 id="原子性"><a href="#原子性" class="headerlink" title="原子性"></a>原子性</h2><h3 id="原子性概念"><a href="#原子性概念" class="headerlink" title="原子性概念"></a>原子性概念</h3><p>原子性（Atomicity）：在一次或多次操作中，要么所有的操作都成功执行并且不会受其他因素干扰而中 断，要么所有的操作都不执行或全部执行失败。不会出现中间状态</p>
<h3 id="原子性演示"><a href="#原子性演示" class="headerlink" title="原子性演示"></a>原子性演示</h3><p>案例演示:5个线程各执行1000次 i++;</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: 吕</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2019/9/23 15:50</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * 功能描述: volatile不保证原子性的代码验证</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Video05_01</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        MyData03 myData03 = <span class="keyword">new</span> MyData03();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">20</span>; i++) &#123;</span><br><span class="line">             <span class="keyword">new</span> Thread(() -&gt;&#123;</span><br><span class="line">                 <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; <span class="number">1000</span>; j++) &#123;</span><br><span class="line">                     myData03.increment();</span><br><span class="line">                 &#125;</span><br><span class="line">             &#125;,<span class="string">&quot;线程&quot;</span> + String.valueOf(i)).start();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//需要等待上面的20个线程计算完之后再查看计算结果</span></span><br><span class="line">        <span class="keyword">while</span>(Thread.activeCount() &gt; <span class="number">2</span>)&#123;</span><br><span class="line">            Thread.yield();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;20个线程执行完之后num:\t&quot;</span> + myData03.num);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyData03</span></span>&#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">increment</span><span class="params">()</span></span>&#123;</span><br><span class="line">        num++;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>1、控制台输出：（由于并发不安全，每次执行的结果都可能不一样）</p>
<blockquote>
<p>20个线程执行完之后num:    19706</p>
</blockquote>
<p>正常来说，如果保证原子性的话，20个线程执行完，结果应该是20000。控制台输出的值却不是这个，说明出现了原子性的问题。</p>
<p>2、使用javap反汇编class文件，对于num++可以得到下面的字节码指令：</p>
<figure class="highlight"><table><tr><td class="code"><pre><span class="line">9: getstatic     #12                 // Field number:I   取值操作</span><br><span class="line"><span class="number">12</span>: iconst_1 </span><br><span class="line"><span class="number">13</span>: iadd </span><br><span class="line">14: putstatic     #12                 // Field number:I  赋值操作</span><br></pre></td></tr></table></figure>

<p>由此可见num++是由多条语句组成，以上多条指令在一个线程的情况下是不会出问题的，但是在多线程情况下就可能会出现问题。</p>
<p>比如num刚开始值是7。A线程在执行13: iadd时得到num值是8，B线程又执行9: getstatic得到前一个值是7。马上A线程就把8赋值给了num变量。但是B线程已经拿到了之前的值7，B线程是在A线程真正赋值前拿到的num值。即使A线程最终把值真正的赋给了num变量，但是B线程已经走过了getstaitc取值的这一步，B线程会继续在7的基础上进行++操作，最终的结果依然是8。本来两个线程对7进行分别进行++操作，得到的值应该是9，因为并发问题，导致结果是8。</p>
<p>3、并发编程时，会出现原子性问题，当一个线程对共享变量操作到一半时，另外的线程也有可能来操作共 享变量，干扰了前一个线程的操作。</p>
<h2 id="有序性"><a href="#有序性" class="headerlink" title="有序性"></a>有序性</h2><h3 id="有序性概念"><a href="#有序性概念" class="headerlink" title="有序性概念"></a>有序性概念</h3><p>有序性（Ordering）：是指程序中代码的执行顺序，Java在编译时和运行时会对代码进行优化（重排序）来加快速度，会导致程序终的执行顺序不一定就是我们编写代码时的顺序</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">instance = <span class="keyword">new</span> SingletonDemo() 是被分成以下 <span class="number">3</span> 步完成</span><br><span class="line"> memory = allocate();     分配对象内存空间</span><br><span class="line"> instance(memory);        初始化对象</span><br><span class="line"> instance = memory;	   设置 instance 指向刚分配的内存地址，此时 instance != <span class="keyword">null</span></span><br></pre></td></tr></table></figure>

<p>步骤2 和 步骤3 不存在数据依赖关系，重排与否的执行结果单线程中是一样的。这种指令重排是被 Java 允许的。当 3 在前时，instance 不为 null，但实际上初始化工作还没完成，会变成一个返回 null 的getInstance。这时候数据就出现了问题。</p>
<h3 id="有序性演示"><a href="#有序性演示" class="headerlink" title="有序性演示"></a>有序性演示</h3><p>jcstress是java并发压测工具。<a target="_blank" rel="noopener" href="https://wiki.openjdk.java.net/display/CodeTools/jcstress">https://wiki.openjdk.java.net/display/CodeTools/jcstress</a> 修改pom文件，添加依赖：</p>
<figure class="highlight"><table><tr><td class="code"><pre><span class="line">&lt;dependency&gt;   </span><br><span class="line"> &lt;groupId&gt;org.openjdk.jcstress&lt;/groupId&gt;    </span><br><span class="line">&lt;artifactId&gt;jcstress-core&lt;/artifactId&gt;    </span><br><span class="line">&lt;version&gt;$&#123;jcstress.version&#125;&lt;/version&gt; </span><br><span class="line">&lt;/dependency&gt;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> org.openjdk.jcstress.annotations.*;</span><br><span class="line"><span class="keyword">import</span> org.openjdk.jcstress.infra.results.I_Result;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@JCStressTest</span></span><br><span class="line"> <span class="comment">// @Outcome: 如果输出结果是1或4，我们是接受的(ACCEPTABLE)，并打印ok</span></span><br><span class="line"> <span class="meta">@Outcome(id = &#123;&quot;1&quot;, &quot;4&quot;&#125;, expect = Expect.ACCEPTABLE, desc = &quot;ok&quot;)</span></span><br><span class="line"> <span class="comment">//如果输出结果是0，我们是接受的并且感兴趣的，并打印danger</span></span><br><span class="line"> <span class="meta">@Outcome(id = &quot;0&quot;, expect = Expect.ACCEPTABLE_INTERESTING, desc = &quot;danger&quot;)</span></span><br><span class="line"> <span class="meta">@State</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test03Ordering</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">boolean</span> ready = <span class="keyword">false</span>;</span><br><span class="line">    <span class="comment">// 线程1执行的代码</span></span><br><span class="line">    <span class="meta">@Actor</span> <span class="comment">//@Actor：表示会有多个线程来执行这个方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">actor1</span><span class="params">(I_Result r)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (ready) &#123;</span><br><span class="line">            r.r1 = num + num;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            r.r1 = <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 线程2执行的代码</span></span><br><span class="line">    <span class="comment">// @Actor</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">actor2</span><span class="params">(I_Result r)</span> </span>&#123;</span><br><span class="line">        num = <span class="number">2</span>;</span><br><span class="line">        ready = <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>1、实际上上面两个方法会有很多线程来执行，为了讲解方便，我们只提出线程1和线程2来讲解。</p>
<p>2、I_Result 是一个保存int类型数据的对象，有一个属性 r1 用来保存结果，在多线程情况下可能出现几种结果？</p>
<p>情况1：线 程1先执行actor1，这时ready = false，所以进入else分支结果为1。</p>
<p>情况2：线程2执行到actor2，执行了num = 2;和ready = true，线程1执行，这回进入 if 分支，结果为 4。</p>
<p>情况3：线程2先执行actor2，只执行num = 2；但没来得及执行 ready = true，线程1执行，还是进入 else分支，结果为1。 </p>
<p>情况4：0，发生了指令重排</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 线程2执行的代码</span></span><br><span class="line">   <span class="comment">// @Actor</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">actor2</span><span class="params">(I_Result r)</span> </span>&#123;</span><br><span class="line">       num = <span class="number">2</span>;    <span class="comment">//pos_1</span></span><br><span class="line">       ready = <span class="keyword">true</span>;<span class="comment">//pos_2</span></span><br><span class="line">   &#125;</span><br><span class="line">  </span><br></pre></td></tr></table></figure>

<p>pos_1处代码和pos_2处代码没有什么数据依赖关系，或者说没有因果关系。Java可能对其进行指令重排，排成下面的顺序。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 线程2执行的代码</span></span><br><span class="line">   <span class="comment">// @Actor</span></span><br><span class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">actor2</span><span class="params">(I_Result r)</span> </span>&#123;</span><br><span class="line">   	ready = <span class="keyword">true</span>;<span class="comment">//pos_2</span></span><br><span class="line">       num = <span class="number">2</span>;    <span class="comment">//pos_1</span></span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<p>此时如果线程2先执行到<code>ready = true;</code>还没来得及执行 <code>num = 2;</code> 。线程1执行，直接进入if分支，此时num默认值为0。 得到的结果也就是0。</p>
<h1 id="volatile"><a href="#volatile" class="headerlink" title="volatile"></a>volatile</h1><blockquote>
<p>1、关于可见性，重排序等等的硬件原理，MESI缓存一致性，内存屏障，JMM等等这些，请看我的后面文章。第一阶段只是介绍下用法，不涉及原理。</p>
<p>2、如果你在第一篇文章没有找到你想要的内容，请看我后面的内容。并发的体系，我自认为讲的还是比较全面的。</p>
</blockquote>
<h2 id="volatile保证可见性代码"><a href="#volatile保证可见性代码" class="headerlink" title="volatile保证可见性代码"></a>volatile保证可见性代码</h2><blockquote>
<p>读者可以把两个代码运行一下，就能明显看到不加volatile的死循环（就是程序一直显示没结束）</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* 笔记</span></span><br><span class="line"><span class="comment"> * 1.当没有加Volatile的时候,while循环会一直在里面转圈</span></span><br><span class="line"><span class="comment"> * 2.当加了之后Volatile,由于可见性,一旦num改了之后,就会通知其他线程</span></span><br><span class="line"><span class="comment"> * 3.还有注意的时候不能用if,if不会重新拉回来再判断一次</span></span><br><span class="line"><span class="comment"> * */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Video04_02</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        MyData2 myData = <span class="keyword">new</span> MyData2();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt;&#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t come in &quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                TimeUnit.SECONDS.sleep(<span class="number">3</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//睡3秒之后再修改num,防止A线程先修改了num,那么到while循环的时候就会直接跳出去了</span></span><br><span class="line">            myData.addTo60();</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t come out&quot;</span>);</span><br><span class="line">        &#125;,<span class="string">&quot;A&quot;</span>).start();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span>(myData.num == <span class="number">0</span>)&#123;</span><br><span class="line">            <span class="comment">//只有当num不等于0的时候,才会跳出循环</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyData2</span></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">volatile</span> <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addTo60</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.num = <span class="number">60</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="volatile保证有序性代码"><a href="#volatile保证有序性代码" class="headerlink" title="volatile保证有序性代码"></a>volatile保证有序性代码</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> org.openjdk.jcstress.annotations.*;</span><br><span class="line"><span class="keyword">import</span> org.openjdk.jcstress.infra.results.I_Result;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@JCStressTest</span></span><br><span class="line"> <span class="comment">// @Outcome: 如果输出结果是1或4，我们是接受的(ACCEPTABLE)，并打印ok</span></span><br><span class="line"> <span class="meta">@Outcome(id = &#123;&quot;1&quot;, &quot;4&quot;&#125;, expect = Expect.ACCEPTABLE, desc = &quot;ok&quot;)</span></span><br><span class="line"> <span class="comment">//如果输出结果是0，我们是接受的并且感兴趣的，并打印danger</span></span><br><span class="line"> <span class="meta">@Outcome(id = &quot;0&quot;, expect = Expect.ACCEPTABLE_INTERESTING, desc = &quot;danger&quot;)</span></span><br><span class="line"> <span class="meta">@State</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test03Ordering</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">volatile</span> <span class="keyword">int</span> num = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">volatile</span> <span class="keyword">boolean</span> ready = <span class="keyword">false</span>;</span><br><span class="line">    <span class="comment">// 线程1执行的代码</span></span><br><span class="line">    <span class="meta">@Actor</span> <span class="comment">//@Actor：表示会有多个线程来执行这个方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">actor1</span><span class="params">(I_Result r)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (ready) &#123;</span><br><span class="line">            r.r1 = num + num;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            r.r1 = <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 线程2执行的代码</span></span><br><span class="line">    <span class="comment">// @Actor</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">actor2</span><span class="params">(I_Result r)</span> </span>&#123;</span><br><span class="line">        num = <span class="number">2</span>;</span><br><span class="line">        ready = <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>读者可以将运行结果对比着来看，就能发现区别。</p>
<p>volatile只能保证可见性和有序性（禁止指令重排），无法保证原子性。</p>
<h1 id="CAS"><a href="#CAS" class="headerlink" title="CAS"></a>CAS</h1><p>volatile自己虽然不能保证原子性，但是和CAS结合起来就可以保证原子性了。CAS+volatile一起用就可以同时解决<strong>并发编程中的三个问题</strong>了，保证并发安全。</p>
<h2 id="CAS-是什么？"><a href="#CAS-是什么？" class="headerlink" title="CAS 是什么？"></a>CAS 是什么？</h2><ul>
<li><p>CAS：比较并交换compareAndSet,它是一条 CPU 并发原语，它的功能是判断内存某个位置的值是否为预期值，如果是则更改为新的值，这个过程是原子性的。</p>
</li>
<li><p>例: AtomicInteger 的 compareAndSet(‘期望值’,’设置值’) 方法，期望值与目标值一致时，修改目标变量为设置值，期望值与目标值不一致时，返回 false 和最新主存的变量值</p>
</li>
<li><p>CAS 的底层原理</p>
<pre><code>例: AtomicInteger.getAndIncrement()
  调用 Unsafe 类中的 CAS 方法，JVM 会帮我们实现出 CAS 汇编指令
      这是一种完全依赖于硬件的功能，通过它实现原子操作。
      原语的执行必须是连续的，在执行过程中不允许被中断，CAS 是 CPU 的一条原子指令。
</code></pre>
</li>
<li><p>CAS的思想就是乐观锁的思想</p>
</li>
</ul>
<h2 id="AtomicInteger"><a href="#AtomicInteger" class="headerlink" title="AtomicInteger"></a>AtomicInteger</h2><p>在JUC并发包中，CAS和AtomicInteger（原子类的value值都被volatile修饰了）一起保证了并发安全。下面我们以AtomicInteger.getAndIncrement() 方法讲一下。</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0009.png">



<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * unsafe: rt.jar/sun/misc/Unsafe.class</span></span><br><span class="line"><span class="comment"> *   Unsafe 是 CAS 的核心类，由于 Java 无法直接访问底层系统，需要通过本地&lt;native&gt;方法来访问</span></span><br><span class="line"><span class="comment"> *	 Unsafe 相当于一个后门，基于该类可以直接操作特定内存的数据</span></span><br><span class="line"><span class="comment"> *	 Unsafe 其内部方法都是 native 修饰的，可以像 C 的指针一样直接操作内存</span></span><br><span class="line"><span class="comment"> *	 Java 中的 CAS 操作执行依赖于 Unsafe 的方法，直接调用操作系统底层资源执行程序</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * this: 当前对象</span></span><br><span class="line"><span class="comment"> *	 变量 value 由 volatile 修饰，保证了多线程之间的内存可见性、禁止重排序</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * valueOffset: 内存地址</span></span><br><span class="line"><span class="comment"> *	 表示该变量值在内存中的偏移地址，因为 Unsafe 就是根据内存偏移地址获取数据</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * 1: 固定写死，原值加1</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">getAndIncrement</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> unsafe.getAndAddInt(<span class="keyword">this</span>,valueOffset,<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Unsafe.getAndAddInt()</span></span><br><span class="line"><span class="comment"> * getIntVolatile: 通过内存地址去主存中取对应数据</span></span><br><span class="line"><span class="comment"> * </span></span><br><span class="line"><span class="comment"> * while(!this.compareAndSwapInt(var1,var2,var5,var5 + var4)):</span></span><br><span class="line"><span class="comment"> * 	 将本地 value 与主存中取出的数据对比，如果相同，对其作运算，</span></span><br><span class="line"><span class="comment"> * 		此时返回 true，取反后 while 结束，返回最终值。</span></span><br><span class="line"><span class="comment"> * 	 如果不相同，此时返回 false，取反后 while 循环继续运行，此时为自旋锁&lt;重复尝试&gt;</span></span><br><span class="line"><span class="comment"> *		由于 value 是被 volatile 修饰的，所以拿到主存中最新值，再循环直至成功。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">getAndAddInt</span><span class="params">(Object var1,<span class="keyword">long</span> var2,<span class="keyword">int</span> var4)</span></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> var5;</span><br><span class="line">    <span class="keyword">do</span>&#123;</span><br><span class="line">        var5 = <span class="keyword">this</span>.getIntVolatile(var1,var2); <span class="comment">// 从主存中拷贝变量到本地内存</span></span><br><span class="line">    &#125; <span class="keyword">while</span>(!<span class="keyword">this</span>.compareAndSwapInt(var1,var2,var5,var5 + var4));</span><br><span class="line">    <span class="keyword">return</span> var5;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="CAS-代码演示"><a href="#CAS-代码演示" class="headerlink" title="CAS 代码演示"></a>CAS 代码演示</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CASDemo</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        AtomicInteger num = <span class="keyword">new</span> AtomicInteger(<span class="number">5</span>);</span><br><span class="line">        <span class="comment">// TODO</span></span><br><span class="line">        System.out.println(num.compareAndSet(<span class="number">5</span>, <span class="number">1024</span>) + <span class="string">&quot;\t current num&quot;</span> + num.get());</span><br><span class="line">        System.out.println(num.compareAndSet(<span class="number">5</span>, <span class="number">2019</span>) + <span class="string">&quot;\t current num&quot;</span> + num.get());</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>



<h2 id="CAS三大问题"><a href="#CAS三大问题" class="headerlink" title="CAS三大问题"></a>CAS三大问题</h2><ul>
<li><p>如果 CAS 长时间一直不成功，会给 CPU 带来很大的开销，在Java的实现中是一直通过while循环自旋CAS获取锁。</p>
</li>
<li><p>只能保证一个共享变量的原子操作</p>
</li>
<li><p>引出了 ABA 问题</p>
</li>
</ul>
<h2 id="ABA问题"><a href="#ABA问题" class="headerlink" title="ABA问题"></a>ABA问题</h2><h3 id="什么是ABA问题？"><a href="#什么是ABA问题？" class="headerlink" title="什么是ABA问题？"></a>什么是ABA问题？</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: 吕</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2019/9/24 16:43</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * 功能描述: CAS引发的ABA问题</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Video19_01</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> AtomicReference&lt;Integer&gt; num = <span class="keyword">new</span> AtomicReference&lt;&gt;(<span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">	</span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt;&#123;</span><br><span class="line">            num.compareAndSet(<span class="number">100</span>, <span class="number">101</span>);</span><br><span class="line">            num.compareAndSet(<span class="number">101</span>,<span class="number">100</span>);</span><br><span class="line">        &#125;,<span class="string">&quot;线程A&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt;&#123;</span><br><span class="line">            <span class="comment">//保证A线程已经修改完</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                TimeUnit.SECONDS.sleep(<span class="number">1</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">boolean</span> b = num.compareAndSet(<span class="number">100</span>, <span class="number">2019</span>);</span><br><span class="line">            System.out.println(b + <span class="string">&quot;\t 当前最新值&quot;</span> + num.get().toString());</span><br><span class="line">        &#125;,<span class="string">&quot;线程B&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>CAS 会导致 ABA 问题：</strong></p>
<p>例: A、B线程从主存取出变量 value</p>
<p>-&gt; A 在 N次计算中改变 value 的值<br>-&gt; A 最终计算结果还原 value 最初的值<br>-&gt; B 计算后，比较主存值与自身 value 值一致，修改成功</p>
<p>尽管各个线程的 CAS 都操作成功，但是并不代表这个过程就是没有问题的。</p>
<h3 id="ABA问题的解决"><a href="#ABA问题的解决" class="headerlink" title="ABA问题的解决"></a>ABA问题的解决</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Author</span>: 吕</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2019/9/24 16:49</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * 功能描述: ABA问题的解决</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Video19_02</span> </span>&#123;</span><br><span class="line">    <span class="keyword">static</span> AtomicStampedReference&lt;Integer&gt; num = <span class="keyword">new</span> AtomicStampedReference&lt;&gt;(<span class="number">100</span>,<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> stamp = num.getStamp();<span class="comment">//初始版本号</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt;&#123;</span><br><span class="line">            num.compareAndSet(<span class="number">100</span>,<span class="number">101</span>,num.getStamp(),num.getStamp() + <span class="number">1</span>);</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t 版本号&quot;</span> + num.getStamp());</span><br><span class="line">            num.compareAndSet(<span class="number">101</span>,<span class="number">100</span>,num.getStamp(),num.getStamp() + <span class="number">1</span>);</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;\t 版本号&quot;</span> + num.getStamp());</span><br><span class="line">        &#125;,<span class="string">&quot;线程A&quot;</span>).start();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(() -&gt;&#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                TimeUnit.SECONDS.sleep(<span class="number">3</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">boolean</span> b = num.compareAndSet(<span class="number">100</span>, <span class="number">209</span>, stamp, num.getStamp() + <span class="number">1</span>);</span><br><span class="line">            System.out.println(b + <span class="string">&quot;\t 当前版本号: \t&quot;</span> + num.getStamp());</span><br><span class="line">            System.out.println(<span class="string">&quot;当前最新值 \t&quot;</span> + num.getReference().toString());</span><br><span class="line">        &#125;,<span class="string">&quot;线程B&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>思想很简单，可以很明显的看出来用版本号的方式解决了ABA的问题。</p>
<ul>
<li>除了对象值，AtomicStampedReference内部还维护了一个“状态戳”。</li>
<li>状态戳可类比为时间戳，是一个整数值，每一次修改对象值的同时，也要修改状态戳，从而区分相同对象值的不同状态。</li>
<li>当AtomicStampedReference设置对象值时，对象值以及状态戳都必须满足期望值，写入才会成功。</li>
</ul>
<h2 id="只能保证一个共享变量的原子操作"><a href="#只能保证一个共享变量的原子操作" class="headerlink" title="只能保证一个共享变量的原子操作"></a>只能保证一个共享变量的原子操作</h2><ul>
<li>当对一个共享变量执行操作时，我们可以使用循环CAS的方式来保证原子操作，但是多个共享变量操作时，循环CAS就无法保证操作的原子性，这个时候就可以用锁。还有一个方法，就是把多个共享变量合并成一个共享变量来操作。比如，有两个共享变量i=2,j=a合并一下ij=2a，然后用CAS来操作ij。从java1.5开始，JDK提供了AtomicReference类来保证引用对象之间的原子性，就可以把多个变量放在一个对象里来进行CAS操作。</li>
<li>所以一般来说为了同时解决ABA问题和只能保证一个共享变量，原子类使用时大部分使用的是<code>AtomicStampedReference</code></li>
</ul>
<h1 id="UnSafe"><a href="#UnSafe" class="headerlink" title="UnSafe"></a>UnSafe</h1><p>Unsafe类是在sun.misc包下，不属于Java标准。但是很多Java的基础类库，包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的，比如Netty、Cassandra、Hadoop、Kafka等。Unsafe类在提升Java运行效率，增强Java语言底层操作能力方面起了很大的作用。</p>
<p>Java和C++语言的一个重要区别就是Java中我们无法直接操作一块内存区域，不能像C++中那样可以自己申请内存和释放内存。 <strong>Java中的Unsafe类为我们提供了类似C++手动管理内存的能力，同时也有了指针的问题。</strong></p>
<p>首先，Unsafe类是”final”的，不允许继承。且构造函数是private的:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">Unsafe</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Unsafe theUnsafe;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> INVALID_FIELD_OFFSET = -<span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">registerNatives</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">Unsafe</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line">    ...</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>因此我们无法在外部对Unsafe进行实例化。</strong></p>
<h2 id="获取Unsafe"><a href="#获取Unsafe" class="headerlink" title="获取Unsafe"></a>获取Unsafe</h2><p>Unsafe无法实例化，那么怎么获取Unsafe呢？答案就是通过反射来获取Unsafe：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Unsafe <span class="title">getUnsafe</span><span class="params">()</span> <span class="keyword">throws</span> IllegalAccessException </span>&#123;</span><br><span class="line">    Field unsafeField = Unsafe.class.getDeclaredFields()[<span class="number">0</span>];</span><br><span class="line">    unsafeField.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">    Unsafe unsafe = (Unsafe) unsafeField.get(<span class="keyword">null</span>);</span><br><span class="line">    <span class="keyword">return</span> unsafe;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Unsafe的功能如下图：</p>
<img src= "" data-lazy-src="https://upyunimg.imlql.cn/youthlql@1.0.8/Java_concurrency/Source_code/First_stage/0008.png">

<h2 id="CAS相关"><a href="#CAS相关" class="headerlink" title="CAS相关"></a>CAS相关</h2><p>JUC中大量运用了CAS操作，可以说CAS操作是JUC的基础，因此CAS操作是非常重要的。Unsafe中提供了int,long和Object的CAS操作：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">compareAndSwapObject</span><span class="params">(Object var1, <span class="keyword">long</span> var2, Object var4, Object var5)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">compareAndSwapInt</span><span class="params">(Object var1, <span class="keyword">long</span> var2, <span class="keyword">int</span> var4, <span class="keyword">int</span> var5)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">compareAndSwapLong</span><span class="params">(Object var1, <span class="keyword">long</span> var2, <span class="keyword">long</span> var4, <span class="keyword">long</span> var6)</span></span>;</span><br></pre></td></tr></table></figure>

<h2 id="偏移量相关"><a href="#偏移量相关" class="headerlink" title="偏移量相关"></a>偏移量相关</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">long</span> <span class="title">staticFieldOffset</span><span class="params">(Field var1)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">long</span> <span class="title">objectFieldOffset</span><span class="params">(Field var1)</span></span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ul>
<li>staticFieldOffset方法用于获取静态属性Field在对象中的偏移量，读写静态属性时必须获取其偏移量。</li>
<li>objectFieldOffset方法用于获取非静态属性Field在对象实例中的偏移量，读写对象的非静态属性时会用到这个偏移量</li>
</ul>
<h2 id="类加载"><a href="#类加载" class="headerlink" title="类加载"></a>类加载</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">native</span> Class&lt;?&gt; defineClass(String var1, <span class="keyword">byte</span>[] var2, <span class="keyword">int</span> var3, <span class="keyword">int</span> var4, ClassLoader var5, ProtectionDomain var6);</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">native</span> Class&lt;?&gt; defineAnonymousClass(Class&lt;?&gt; var1, <span class="keyword">byte</span>[] var2, Object[] var3);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> Object <span class="title">allocateInstance</span><span class="params">(Class&lt;?&gt; var1)</span> <span class="keyword">throws</span> InstantiationException</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">shouldBeInitialized</span><span class="params">(Class&lt;?&gt; var1)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">ensureClassInitialized</span><span class="params">(Class&lt;?&gt; var1)</span></span>;</span><br></pre></td></tr></table></figure>

<ul>
<li>defineClass方法定义一个类，用于动态地创建类。</li>
<li>defineAnonymousClass用于动态的创建一个匿名内部类。</li>
<li><code>allocateInstance</code>方法用于创建一个类的实例，但是不会调用这个实例的构造方法，如果这个类还未被初始化，则初始化这个类。</li>
<li>shouldBeInitialized方法用于判断是否需要初始化一个类。</li>
<li>ensureClassInitialized方法用于保证已经初始化过一个类。</li>
</ul>
<p><strong>举例</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UnsafeFooTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Unsafe <span class="title">geUnsafe</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Field f = Unsafe.class.getDeclaredField(<span class="string">&quot;theUnsafe&quot;</span>);</span><br><span class="line">            f.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">            <span class="keyword">return</span> (Unsafe) f.get(<span class="keyword">null</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (NoSuchFieldException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IllegalAccessException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Simple</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">long</span> l = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">Simple</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">this</span>.l = <span class="number">1</span>;</span><br><span class="line">            System.out.println(<span class="string">&quot;我被初始化了&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">getL</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            <span class="keyword">return</span> l;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line"></span><br><span class="line">        Unsafe unsafe = geUnsafe();</span><br><span class="line"></span><br><span class="line">        Simple s = (Simple) unsafe.allocateInstance(Simple.class);</span><br><span class="line">        System.out.println(s.getL());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>结果：</strong></p>
<blockquote>
<p>0</p>
</blockquote>
<ul>
<li>可以发现，利用Unsafe获取实例，不会调用构造方法</li>
</ul>
<h2 id="普通读写"><a href="#普通读写" class="headerlink" title="普通读写"></a>普通读写</h2><p>通过Unsafe可以读写一个类的属性，即使这个属性是私有的，也可以对这个属性进行读写。</p>
<p>读写一个Object属性的相关方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">int</span> <span class="title">getInt</span><span class="params">(Object var1, <span class="keyword">long</span> var2)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">putInt</span><span class="params">(Object var1, <span class="keyword">long</span> var2, <span class="keyword">int</span> var4)</span></span>;</span><br></pre></td></tr></table></figure>

<ul>
<li>getInt用于从对象的指定偏移地址处读取一个int。</li>
<li>putInt用于在对象指定偏移地址处写入一个int。其他的primitive type也有对应的方法。</li>
</ul>
<p><strong>举例</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UnsafeFooTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> Unsafe <span class="title">geUnsafe</span><span class="params">()</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Field f = Unsafe.class.getDeclaredField(<span class="string">&quot;theUnsafe&quot;</span>);</span><br><span class="line">            f.setAccessible(<span class="keyword">true</span>);</span><br><span class="line">            <span class="keyword">return</span> (Unsafe) f.get(<span class="keyword">null</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (NoSuchFieldException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IllegalAccessException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Guard</span></span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">int</span> ACCESS_ALLOWED = <span class="number">1</span>;</span><br><span class="line">        <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">allow</span><span class="params">()</span></span>&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">50</span> == ACCESS_ALLOWED;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">work</span><span class="params">()</span></span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (allow())&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;我被允许工作....&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        Unsafe unsafe = geUnsafe();</span><br><span class="line">        Guard guard = <span class="keyword">new</span> Guard();</span><br><span class="line"></span><br><span class="line">        Field f = guard.getClass().getDeclaredField(<span class="string">&quot;ACCESS_ALLOWED&quot;</span>);</span><br><span class="line"></span><br><span class="line">        unsafe.putInt(guard,unsafe.objectFieldOffset(f),<span class="number">50</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;强行赋值...&quot;</span>);</span><br><span class="line">        guard.work();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><strong>结果</strong></p>
<blockquote>
<p>强行赋值…</p>
</blockquote>
<p>我被允许工作…</p>
<h2 id="类加载-1"><a href="#类加载-1" class="headerlink" title="类加载"></a>类加载</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">native</span> Class&lt;?&gt; defineClass(String var1, <span class="keyword">byte</span>[] var2, <span class="keyword">int</span> var3, <span class="keyword">int</span> var4, ClassLoader var5, ProtectionDomain var6);</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">native</span> Class&lt;?&gt; defineAnonymousClass(Class&lt;?&gt; var1, <span class="keyword">byte</span>[] var2, Object[] var3);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> Object <span class="title">allocateInstance</span><span class="params">(Class&lt;?&gt; var1)</span> <span class="keyword">throws</span> InstantiationException</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">shouldBeInitialized</span><span class="params">(Class&lt;?&gt; var1)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">ensureClassInitialized</span><span class="params">(Class&lt;?&gt; var1)</span></span>;</span><br></pre></td></tr></table></figure>

<ul>
<li>defineClass方法定义一个类，用于动态地创建类。</li>
<li>defineAnonymousClass用于动态的创建一个匿名内部类。</li>
<li>allocateInstance方法用于创建一个类的实例，但是不会调用这个实例的构造方法，如果这个类还未被初始化，则初始化这个类。</li>
<li>shouldBeInitialized方法用于判断是否需要初始化一个类。</li>
<li>ensureClassInitialized方法用于保证已经初始化过一个类。</li>
</ul>
<h2 id="内存屏障"><a href="#内存屏障" class="headerlink" title="内存屏障"></a>内存屏障</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">loadFence</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">storeFence</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">fullFence</span><span class="params">()</span></span>;</span><br></pre></td></tr></table></figure>

<ul>
<li>loadFence：保证在这个屏障之前的所有读操作都已经完成。</li>
<li>storeFence：保证在这个屏障之前的所有写操作都已经完成。</li>
<li>fullFence：保证在这个屏障之前的所有读写操作都已经完成。</li>
</ul>
<h2 id="线程调度"><a href="#线程调度" class="headerlink" title="线程调度"></a>线程调度</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">unpark</span><span class="params">(Object var1)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">park</span><span class="params">(<span class="keyword">boolean</span> var1, <span class="keyword">long</span> var2)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">monitorEnter</span><span class="params">(Object var1)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">void</span> <span class="title">monitorExit</span><span class="params">(Object var1)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">tryMonitorEnter</span><span class="params">(Object var1)</span></span>;</span><br></pre></td></tr></table></figure>

<ul>
<li>park方法和unpark方法相信看过LockSupport类的都不会陌生，这两个方法主要用来挂起和唤醒线程。</li>
<li>LockSupport中的park和unpark方法正是通过Unsafe来实现的：</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">park</span><span class="params">(Object blocker)</span> </span>&#123;</span><br><span class="line">    Thread t = Thread.currentThread();</span><br><span class="line">    setBlocker(t, blocker);</span><br><span class="line">    UNSAFE.park(<span class="keyword">false</span>, <span class="number">0L</span>);</span><br><span class="line">    setBlocker(t, <span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">unpark</span><span class="params">(Thread thread)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (thread != <span class="keyword">null</span>)</span><br><span class="line">        UNSAFE.unpark(thread);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>monitorEnter方法和monitorExit方法用于加锁，Java中的synchronized锁就是通过这两个指令来实现的。</strong></p>
<h1 id="synchronized优化"><a href="#synchronized优化" class="headerlink" title="synchronized优化"></a>synchronized优化</h1><blockquote>
<p>synchronized可以同时保证可见性，有序性，原子性。这个东西就不讲了</p>
</blockquote>
<p>从JDk 1.6开始，JVM就对synchronized锁进行了很多的优化。synchronized说是锁，但是他的底层加锁的方式可能不同，偏向锁的方式来加锁，自旋锁的方式来加锁，轻量级锁的方式来加锁</p>
<h2 id="锁消除"><a href="#锁消除" class="headerlink" title="锁消除"></a>锁消除</h2><p>锁消除是JIT编译器对synchronized锁做的优化，在编译的时候，JIT会通过逃逸分析技术，来分析synchronized锁对象，是不是只可能被一个线程来加锁，没有其他的线程来竞争加锁，这个时候编译就不用加入monitorenter和monitorexit的指令。这就是，仅仅一个线程争用锁的时候，就可以消除这个锁了，提升这段代码的执行的效率，因为可能就只有一个线程会来加锁，不涉及到多个线程竞争锁</p>
<h2 id="锁粗化"><a href="#锁粗化" class="headerlink" title="锁粗化"></a>锁粗化</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">synchronized</span>(<span class="keyword">this</span>) &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"><span class="keyword">synchronized</span>(<span class="keyword">this</span>) &#123; </span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"><span class="keyword">synchronized</span>(<span class="keyword">this</span>) &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>这个意思就是，JIT编译器如果发现有代码里连续多次加锁释放锁的代码，会给合并为一个锁，就是锁粗化，把一个锁给搞粗了，避免频繁多次加锁释放锁</p>
<h2 id="偏向锁"><a href="#偏向锁" class="headerlink" title="偏向锁"></a>偏向锁</h2><p>这个意思就是说，monitorenter和monitorexit是要使用CAS操作加锁和释放锁的，开销较大，因此如果发现大概率只有一个线程会主要竞争一个锁，那么会给这个锁维护一个偏好（Bias），后面他加锁和释放锁，基于Bias来执行，不需要通过CAS，性能会提升很多。但是如果有偏好之外的线程来竞争锁，就要收回之前分配的偏好。可能只有一个线程会来竞争一个锁，但是也有可能会有其他的线程来竞争这个锁，但是其他线程唉竞争锁的概率很小。如果有其他的线程来竞争这个锁，此时就会收回之前那个线程分配的那个Bias偏好</p>
<h2 id="轻量级锁"><a href="#轻量级锁" class="headerlink" title="轻量级锁"></a>轻量级锁</h2><p>如果偏向锁没能成功实现，就是因为不同线程竞争锁太频繁了，此时就会尝试采用轻量级锁的方式来加锁，就是将对象头的Mark Word里有一个轻量级锁指针，尝试指向持有锁的线程，然后判断一下是不是自己加的锁，如果是自己加的锁，那就执行代码就好了。如果不是自己加的锁，那就是加锁失败，说明有其他人加了锁，这个时候就是升级为重量级锁</p>
<h2 id="适应性锁"><a href="#适应性锁" class="headerlink" title="适应性锁"></a>适应性锁</h2><p>这是JIT编译器对锁做的另外一个优化，如果各个线程持有锁的时间很短，那么一个线程竞争锁不到，就会暂停，发生上下文切换，让其他线程来执行。但是其他线程很快释放锁了，然后暂停的线程再次被唤醒。也就是说在这种情况下，线程会频繁的上下文切换，导致开销过大。所以对这种线程持有锁时间很短的情况，是可以采取忙等策略的，也就是一个线程没竞争到锁，进入一个while循环不停等待，不会暂停不会发生线程上下文切换，等到机会获取锁就继续执行好了</p>
<h1 id="参考："><a href="#参考：" class="headerlink" title="参考："></a>参考：</h1><ul>
<li><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_43040688/article/details/103979628">https://blog.csdn.net/qq_43040688/article/details/103979628</a></li>
<li>《Java 并发编程艺术》</li>
<li>B站汪文君系列并发</li>
</ul>
<h1 id="强调"><a href="#强调" class="headerlink" title="强调"></a>强调</h1><p>1、第一阶段只是简单的讲一下，在后面的系列里，会从硬件，C++源码层面讲解volatile，synchronized，内存屏障，MESI-缓存一致性等等进行讲解。</p>
<p>2、还有一些问题，在基础阶段可能不太好讲。比如中断这个东西，可能理解的云里雾里的，后面的系列讲到AQS的时候，结合Java源码再讲的话，你会非常好理解。</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href="mailto:undefined">youthlql</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://imlql.cn/post/efc79183.html">https://imlql.cn/post/efc79183.html</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://imlql.cn" target="_blank">风祈的时光录</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/Java%E5%B9%B6%E5%8F%91/">Java并发</a><a class="post-meta__tags" href="/tags/%E5%8E%9F%E7%90%86/">原理</a><a class="post-meta__tags" href="/tags/%E6%BA%90%E7%A0%81/">源码</a></div><div class="post_share"></div></div><div class="post-reward"><div class="reward-button"><i class="fas fa-qrcode"></i> 打赏</div><div class="reward-main"><ul class="reward-all"><li class="reward-item"><a href="https://npm.elemecdn.com/youthlql@1.0.8/blog/wechat.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="https://npm.elemecdn.com/youthlql@1.0.8/blog/wechat.jpg" alt="微信"/></a><div class="post-qr-code-desc">微信</div></li><li class="reward-item"><a href="https://npm.elemecdn.com/youthlql@1.0.8/blog/alipay.jpg" target="_blank"><img class="post-qr-code-img" src= "" data-lazy-src="https://npm.elemecdn.com/youthlql@1.0.8/blog/alipay.jpg" alt="支付宝"/></a><div class="post-qr-code-desc">支付宝</div></li></ul></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-full"><a href="/post/230c5bb3.html"><img class="prev-cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">Java并发体系-第二阶段-锁与同步-[1]</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/post/8210870.html" title="Java并发体系-第二阶段-锁与同步-[2]"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-07</div><div class="title">Java并发体系-第二阶段-锁与同步-[2]</div></div></a></div><div><a href="/post/230c5bb3.html" title="Java并发体系-第二阶段-锁与同步-[1]"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-06</div><div class="title">Java并发体系-第二阶段-锁与同步-[1]</div></div></a></div><div><a href="/post/5be45d9e.html" title="Java并发体系-第三阶段-JUC并发包-[1]"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-09</div><div class="title">Java并发体系-第三阶段-JUC并发包-[1]</div></div></a></div><div><a href="/post/113a3931.html" title="Java并发体系-第二阶段-锁与同步-[3]"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-08</div><div class="title">Java并发体系-第二阶段-锁与同步-[3]</div></div></a></div><div><a href="/post/70c90e5d.html" title="Java并发体系-第三阶段-JUC并发包-[2]"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-10</div><div class="title">Java并发体系-第三阶段-JUC并发包-[2]</div></div></a></div><div><a href="/post/92c4503d.html" title="Java并发体系-第四阶段-AQS源码解读-[1]"><img class="cover" src= "" data-lazy-src="https://upyunimg.imlql.cn/lql_static@latest/logo/Java_concurrency.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2020-10-26</div><div class="title">Java并发体系-第四阶段-AQS源码解读-[1]</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content is-expand"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E7%A8%8B%E5%BA%8F%E3%80%81%E8%BF%9B%E7%A8%8B%E3%80%81%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%90%86%E8%A7%A3"><span class="toc-text">程序、进程、线程的理解</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%B9%B6%E8%A1%8C%E4%B8%8E%E5%B9%B6%E5%8F%91"><span class="toc-text">并行与并发</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8D%95%E6%A0%B8CPU%E4%B8%8E%E5%A4%9A%E6%A0%B8CPU%E7%9A%84%E7%90%86%E8%A7%A3"><span class="toc-text">单核CPU与多核CPU的理解</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%B9%B6%E8%A1%8C%E4%B8%8E%E5%B9%B6%E5%8F%91%E7%9A%84%E7%90%86%E8%A7%A3"><span class="toc-text">并行与并发的理解</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%87%A0%E7%A7%8D%E6%96%B9%E6%B3%95"><span class="toc-text">创建线程的几种方法</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%A7%E6%89%BFThread%E7%B1%BB%E5%88%9B%E5%BB%BA%E7%BA%BF%E7%A8%8B"><span class="toc-text">继承Thread类创建线程</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AE%9E%E7%8E%B0Runnable%E6%8E%A5%E5%8F%A3%E5%88%9B%E5%BB%BA%E7%BA%BF%E7%A8%8B"><span class="toc-text">实现Runnable接口创建线程</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Thread%E5%92%8CRunnable%E7%9A%84%E5%85%B3%E7%B3%BB"><span class="toc-text">Thread和Runnable的关系</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Runnable%E6%8E%A5%E5%8F%A3%E6%9E%84%E9%80%A0%E7%BA%BF%E7%A8%8B%E6%BA%90%E7%A0%81"><span class="toc-text">Runnable接口构造线程源码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Thread%E7%B1%BB%E6%9E%84%E9%80%A0%E7%BA%BF%E7%A8%8B%E6%BA%90%E7%A0%81"><span class="toc-text">Thread类构造线程源码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%80%E7%9B%B4%E8%A7%82%E7%9A%84%E4%BB%A3%E7%A0%81%E6%8F%8F%E8%BF%B0"><span class="toc-text">最直观的代码描述</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AE%9E%E7%8E%B0Callable%E6%8E%A5%E5%8F%A3%E5%88%9B%E5%BB%BA%E7%BA%BF%E7%A8%8B"><span class="toc-text">实现Callable接口创建线程</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F%E5%9C%A8Thread%E5%92%8CRunnable%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8"><span class="toc-text">策略模式在Thread和Runnable中的应用</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Thread%E7%9A%84%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95"><span class="toc-text">Thread的构造方法</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#start-%E6%BA%90%E7%A0%81"><span class="toc-text">start()源码</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%90%AF%E5%8A%A8%E4%B8%8D%E7%9B%B4%E6%8E%A5%E4%BD%BF%E7%94%A8run-%E8%80%8C%E5%BF%85%E9%A1%BB%E4%BD%BF%E7%94%A8start-%E5%91%A2"><span class="toc-text">为什么线程的启动不直接使用run()而必须使用start()呢?</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F"><span class="toc-text">线程生命周期</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Thread%E5%B8%B8%E7%94%A8API"><span class="toc-text">Thread常用API</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E4%B8%80%E4%B8%AAJava%E7%A8%8B%E5%BA%8F%E6%9C%89%E5%93%AA%E4%BA%9B%E7%BA%BF%E7%A8%8B%EF%BC%9F"><span class="toc-text">一个Java程序有哪些线程？</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%AE%88%E6%8A%A4%E7%BA%BF%E7%A8%8B"><span class="toc-text">守护线程</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%94%A8%E5%A4%84"><span class="toc-text">用处</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#join%E6%96%B9%E6%B3%95"><span class="toc-text">join方法</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E4%B8%AD%E6%96%AD"><span class="toc-text">中断</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%87%A0%E4%B8%AA%E6%96%B9%E6%B3%95"><span class="toc-text">几个方法</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%B0%8Ftip"><span class="toc-text">小tip</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%87%8D%E9%9A%BE%E7%82%B9"><span class="toc-text">重难点</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#InterruptedException"><span class="toc-text">InterruptedException</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#wait-%E4%B8%AD%E6%96%AD%E6%B5%8B%E8%AF%95"><span class="toc-text">wait()中断测试</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#join%E4%B8%AD%E6%96%AD%E6%B5%8B%E8%AF%95"><span class="toc-text">join中断测试</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%85%B3%E9%97%AD%E7%BA%BF%E7%A8%8B"><span class="toc-text">关闭线程</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BC%98%E9%9B%85%E7%9A%84%E5%85%B3%E9%97%AD-%E9%80%9A%E8%BF%87%E4%B8%80%E4%B8%AABoolean"><span class="toc-text">优雅的关闭(通过一个Boolean)</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%80%9A%E8%BF%87%E5%88%A4%E6%96%AD%E4%B8%AD%E6%96%AD%E7%8A%B6%E6%80%81"><span class="toc-text">通过判断中断状态</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9A%B4%E5%8A%9B%E5%85%B3%E9%97%AD%EF%BC%88%E5%AE%88%E6%8A%A4%E7%BA%BF%E7%A8%8B%EF%BC%89"><span class="toc-text">暴力关闭（守护线程）</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B8%AD%E7%9A%84%E4%B8%89%E4%B8%AA%E9%97%AE%E9%A2%98"><span class="toc-text">并发编程中的三个问题</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8F%AF%E8%A7%81%E6%80%A7"><span class="toc-text">可见性</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%AF%E8%A7%81%E6%80%A7%E6%A6%82%E5%BF%B5"><span class="toc-text">可见性概念</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%AF%E8%A7%81%E6%80%A7%E6%BC%94%E7%A4%BA"><span class="toc-text">可见性演示</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8E%9F%E5%AD%90%E6%80%A7"><span class="toc-text">原子性</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8E%9F%E5%AD%90%E6%80%A7%E6%A6%82%E5%BF%B5"><span class="toc-text">原子性概念</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8E%9F%E5%AD%90%E6%80%A7%E6%BC%94%E7%A4%BA"><span class="toc-text">原子性演示</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9C%89%E5%BA%8F%E6%80%A7"><span class="toc-text">有序性</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%89%E5%BA%8F%E6%80%A7%E6%A6%82%E5%BF%B5"><span class="toc-text">有序性概念</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%89%E5%BA%8F%E6%80%A7%E6%BC%94%E7%A4%BA"><span class="toc-text">有序性演示</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#volatile"><span class="toc-text">volatile</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#volatile%E4%BF%9D%E8%AF%81%E5%8F%AF%E8%A7%81%E6%80%A7%E4%BB%A3%E7%A0%81"><span class="toc-text">volatile保证可见性代码</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#volatile%E4%BF%9D%E8%AF%81%E6%9C%89%E5%BA%8F%E6%80%A7%E4%BB%A3%E7%A0%81"><span class="toc-text">volatile保证有序性代码</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#CAS"><span class="toc-text">CAS</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#CAS-%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="toc-text">CAS 是什么？</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#AtomicInteger"><span class="toc-text">AtomicInteger</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#CAS-%E4%BB%A3%E7%A0%81%E6%BC%94%E7%A4%BA"><span class="toc-text">CAS 代码演示</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#CAS%E4%B8%89%E5%A4%A7%E9%97%AE%E9%A2%98"><span class="toc-text">CAS三大问题</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#ABA%E9%97%AE%E9%A2%98"><span class="toc-text">ABA问题</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BB%80%E4%B9%88%E6%98%AFABA%E9%97%AE%E9%A2%98%EF%BC%9F"><span class="toc-text">什么是ABA问题？</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ABA%E9%97%AE%E9%A2%98%E7%9A%84%E8%A7%A3%E5%86%B3"><span class="toc-text">ABA问题的解决</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%8F%AA%E8%83%BD%E4%BF%9D%E8%AF%81%E4%B8%80%E4%B8%AA%E5%85%B1%E4%BA%AB%E5%8F%98%E9%87%8F%E7%9A%84%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C"><span class="toc-text">只能保证一个共享变量的原子操作</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#UnSafe"><span class="toc-text">UnSafe</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%8E%B7%E5%8F%96Unsafe"><span class="toc-text">获取Unsafe</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#CAS%E7%9B%B8%E5%85%B3"><span class="toc-text">CAS相关</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%81%8F%E7%A7%BB%E9%87%8F%E7%9B%B8%E5%85%B3"><span class="toc-text">偏移量相关</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD"><span class="toc-text">类加载</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%99%AE%E9%80%9A%E8%AF%BB%E5%86%99"><span class="toc-text">普通读写</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%B1%BB%E5%8A%A0%E8%BD%BD-1"><span class="toc-text">类加载</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%86%85%E5%AD%98%E5%B1%8F%E9%9A%9C"><span class="toc-text">内存屏障</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E8%B0%83%E5%BA%A6"><span class="toc-text">线程调度</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#synchronized%E4%BC%98%E5%8C%96"><span class="toc-text">synchronized优化</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%94%81%E6%B6%88%E9%99%A4"><span class="toc-text">锁消除</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%94%81%E7%B2%97%E5%8C%96"><span class="toc-text">锁粗化</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%81%8F%E5%90%91%E9%94%81"><span class="toc-text">偏向锁</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%BD%BB%E9%87%8F%E7%BA%A7%E9%94%81"><span class="toc-text">轻量级锁</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E9%80%82%E5%BA%94%E6%80%A7%E9%94%81"><span class="toc-text">适应性锁</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%8F%82%E8%80%83%EF%BC%9A"><span class="toc-text">参考：</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%BC%BA%E8%B0%83"><span class="toc-text">强调</span></a></li></ol></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By youthlql</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text"><a href="https://www.upyun.com/?utm_source=lianmeng&utm_medium=referral" target="_blank" rel="noopener" class="one-pan-link-mark"><img style="position:relative;top:-3px; " src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/upyun/logo.png" align="absmiddle" width="60px" height="30px"></a><a target="_blank" rel="noopener" href="https://beian.miit.gov.cn"><img class="icp-icon" src= "" data-lazy-src="https://npm.elemecdn.com/lql_static@latest/logo/icp.png"><span>鄂ICP备19028890号</span></a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">简</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">本地搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="/js/tw_cn.js"></script><script src="https://npm.elemecdn.com/@fancyapps/ui/dist/fancybox.umd.js"></script><script src="https://npm.elemecdn.com/instant.page/instantpage.js" type="module"></script><script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js"></script><script src="/js/search/local-search.js"></script><div class="js-pjax"></div><script defer src="https://npm.elemecdn.com/jquery@latest/dist/jquery.min.js"></script></div></body></html>